package checker

import (
	"fmt"
	"mi/compiler/ast"
	"mi/compiler/binder"
	"mi/compiler/diagnostic"
	"mi/compiler/token"
	"mi/compiler/utilities"
	"mi/internal/xbase"
	"sort"
	"strconv"
	"unicode/utf8"
)

// EXPRESSION TYPE CHECKING

func (c *checker) getResolvedSymbol(node *ast.Identifier) *binder.Symbol {
	var links = c.getNodeLinks(node)
	if links.resolvedSymbol == nil {
		if !ast.NodeIsMissing(node) {
			links.resolvedSymbol = c.resolveName(node, node.Value, binder.Value, node,
				diagnostic.M_Cannot_find_name_0, ast.DeclarationNameToString(node))
		} else {
			links.resolvedSymbol = c.unknownSymbol
		}
	}
	return links.resolvedSymbol
}

// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
// separated by dots). The key consists of the id of the symbol referenced by the
// leftmost identifier followed by zero or more property names separated by dots.
// The result is undefined if the reference isn't a dotted name.
func (c *checker) getFlowCacheKey(node ast.Node) string {
	switch n := node.(type) {
	case *ast.Identifier:
		var symbol = c.getResolvedSymbol(n)
		if symbol != c.unknownSymbol {
			return strconv.Itoa(GetSymbolId(symbol))
		}
		return ""

	case *ast.ThisExpression:
		return "0"

	case *ast.SelectorExpression:
		var key = c.getFlowCacheKey(n.Expression)
		if len(key) > 0 {
			return key + "." + n.Name.Value
		}
		return ""

	default:
		return ""
	}
}

func (c *checker) isMatchingReference(source ast.Node, target ast.Node) bool {
	switch n := source.(type) {
	case *ast.Identifier:
		return ast.IsIdentifier(target) && c.getResolvedSymbol(n) == c.getResolvedSymbol(target.(*ast.Identifier)) ||
			ast.IsVariableDeclaration(target) && c.getResolvedSymbol(n) == c.getSymbolOfNode(target)
	case *ast.ThisExpression:
		return ast.IsThisExpression(target) && c.isMatchingReference(n.Expression, target.(*ast.ThisExpression).Expression)
	case *ast.SelectorExpression:
		return ast.IsSelectorExpression(target) &&
			n.Name.Value == target.(*ast.SelectorExpression).Name.Value &&
			c.isMatchingReference(n.Expression, target.(*ast.SelectorExpression).Expression)
	}

	return false
}

func (c *checker) isIncomplete(flowType Type) bool {
	return flowType.Flags() == 0
}

func (c *checker) getTypeFromFlowType(flowType Type) Type {
	if flowType.Flags() == 0 {
		return flowType.(*incompleteType).TempType
	}
	return flowType
}

func (c *checker) createFlowType(tpe Type, incomplete bool) Type {
	if incomplete {
		var t = new(incompleteType)
		t.flags = 0
		t.TempType = tpe
		return t
	}
	return tpe
}

type callParameter struct {
	TypeArguments *ast.TypeList
	Arguments     *ast.ExpressionList
}

func (c *checker) getControlFlowContainer(node ast.Node) ast.Node {
	for true {
		node = node.Parent()
		if ast.IsFunctionLike(node) && ast.GetImmediatelyInvokedFunctionExpression(node) == nil ||
			ast.IsFieldDeclaration(node) {
			return node
		}
	}
	return nil
}

func (c *checker) checkReferenceOuterBlockVariable(location ast.Node, symbol *binder.Symbol) {
	if symbol.Flags&binder.Variable != 0 {
		var flags = c.getDeclarationModifierFlagsFromSymbol(symbol)
		if flags&ast.MFFinal != 0 {
			return
		}

		var contain = ast.GetContainingClassExpressionLike(location)
		if !ast.HasAncestorNode(location, contain) {
			c.error(location, diagnostic.M_Variable_is_outer_should_be_final_or_effectively_final)
		}
	}
}

func (c *checker) isDocumentExpression(node ast.Node) bool {
	var current = node
	for !isNull(current) {
		switch n := current.(type) {
		case *ast.SelectorExpression:
			current = current.Parent()
			continue
		case *ast.InstanceAccessExpression:
			current = current.Parent()
			continue
		case *ast.DocumentCallExpression:
			return true
		case *ast.SeeTag:
			return n.Expression == current
		case *ast.CodeLink:
			return n.Expression == current
		}

		break
	}

	return false
}

func (c *checker) checkIdentifier(node *ast.Identifier) Type {
	var location ast.Node = node

	if c.isDocumentExpression(node) {
		location = ast.GetContainerDocRoot(node).Parent()
	}

	var symbol *binder.Symbol
	if ast.IsCallTarget(node) {
		symbol = c.resolveName(location, node.Value, binder.Call, node, diagnostic.M_Cannot_find_name_0, node.Value)
	} else {
		symbol = c.resolveName(location, node.Value, binder.Value, node, diagnostic.M_Cannot_find_name_0, node.Value)
	}

	if symbol == nil {
		return c.unknownType
	}

	c.checkReferenceOuterBlockVariable(node, symbol)
	c.getNodeLinks(node).resolvedSymbol = symbol

	var tpe = c.getTypeOfSymbol(symbol)
	// If types is package type, return it.
	if tpe.Flags()&TFPackage != 0 {
		return tpe
	}

	// Reference in the initialization
	if symbol.Flags&(binder.Variable) != 0 &&
		!isNull(symbol.ValueDeclaration.(ast.VariableLike).GetInitializer()) {
		var target = symbol.ValueDeclaration.(ast.VariableLike).GetInitializer()

		var location ast.Node = node
		for !isNull(location) {
			if location == target {
				c.error(node, diagnostic.M_Variable_0_is_used_before_being_assigned,
					c.symbolToString(symbol, nil, 0, true))
				return c.unknownType
			}

			location = location.Parent()
		}
	}

	var declaration = symbol.ValueDeclaration
	var assignmentKind = ast.GetAssignmentTargetKind(node)
	if assignmentKind != ast.NoAssign {
		if symbol.Flags&(binder.Variable|binder.Field) == 0 {
			c.error(node, diagnostic.M_Cannot_assign_to_0_because_it_is_not_a_variable,
				c.symbolToString(symbol, nil, 0, true))
			return c.unknownType
		}
		if c.isReadOnlySymbol(symbol) {
			c.error(node, diagnostic.M_Cannot_assign_to_0_because_it_is_a_read_only,
				c.symbolToString(symbol, nil, 0, true))
			return c.unknownType
		}
		if symbol.Flags&binder.Variable != 0 &&
			ast.GetContainingFunction(node) != ast.GetContainingFunction(symbol.ValueDeclaration) {
			c.error(node, diagnostic.M_It_is_not_allowed_to_modify_non_local_variables_in_inner_classes)
			return c.unknownType
		}
	}

	// We only narrow variables and parameters occurring in a non-assignment position. For all other
	// entities we simply return the declared type.
	if symbol.Flags&binder.Variable == 0 ||
		assignmentKind == ast.Definite ||
		declaration == nil {
		return tpe
	}

	// The declaration container is the innermost function that encloses the declaration of the variable
	// or parameter. The flow container is the innermost function starting with which we analyze the control
	// flow graph to determine the control flow based type.
	var isParameter = ast.IsParameterDeclaration(declaration)
	var declarationContainer = c.getControlFlowContainer(declaration)
	var flowContainer = c.getControlFlowContainer(node)
	var isOuterVariable = flowContainer != declarationContainer

	// When the control flow originates in a function expression or arrow function and we are referencing
	// a const variable or parameter from an outer function, we extend the origin of the control flow
	// analysis to include the immediately enclosing function.
	for flowContainer != declarationContainer && (ast.IsArrowFunctionDeclaration(flowContainer) ||
		ast.IsClassBodyFunctionLike(flowContainer)) && c.isReadOnlySymbol(symbol) {
		flowContainer = c.getControlFlowContainer(flowContainer)
	}

	// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
	// the entire control flow graph from the variable's declaration (i.e. when the flow container and
	// declaration container are the same).
	var assumeInitialized = isParameter || isOuterVariable || ast.IsInAmbientContext(declaration)
	var flowType = c.getFlowTypeOfReference(node, tpe, assumeInitialized, flowContainer)
	c.getNodeLinks(node).resolvedFlowType = flowType

	// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
	// from declaration to use, and when the variable's declared type doesn't include undefined but the
	// control flow based type does include undefined.
	if !assumeInitialized && flowType == c.undefinedType {
		c.error(node, diagnostic.M_Variable_0_is_used_before_being_assigned,
			c.symbolToString(symbol, nil, 0, true))
	}

	return tpe
}

func (c *checker) checkThisBeforeSuper(node ast.Node, container ast.Node, diagnosticMessage *diagnostic.Message) {
	var containingClassDecl = container.Parent().(ast.ObjectLike)
	var baseTypeNode = ast.GetClassExtendsHeritageClauseElement(containingClassDecl)

	// If a containing class does not have extends clause or the class extends null
	// skip checking whether super statement is called before "this" accessing.
	if baseTypeNode != nil && ast.IsConstructorDeclaration(container){
		var superCall = c.getSuperCallInConstructor(container.(*ast.ConstructorDeclaration))

		// We should give an error in the following cases:
		//      - No super-call
		//      - "this" is accessing before super-call.
		//          i.e super(this)
		//              this.x; super();
		// We want to make sure that super-call is done before accessing "this" so that
		// "this" is not accessed as a parameter of the super-call.
		if superCall == nil || superCall.End() > node.Pos() {
			// In ES6, super inside constructor of class-declaration has to precede "this" accessing
			c.error(node, diagnosticMessage)
		}
	}
}

func (c *checker) checkThisExpression(node *ast.ThisExpression) Type {
	var container = ast.GetThisOrSuperContainer(node)
	if container == nil {
		c.error(node, diagnostic.M_this_cannot_be_referenced_in_current_location)
		return c.unknownType
	}
	var realContainer = container.Parent()

	var resolveType Type
	var needThisBeforeSuper = false
	if node.Expression != nil {
		var symbol = c.resolveEntityName(node, node.Expression, binder.Class, node)
		if symbol == nil {
			c.error(node.Expression, diagnostic.M_Class_name_expected_here)
			return c.unknownType
		}

		resolveType = c.getDeclaredTypeOfSymbol(symbol)

		if resolveType != c.unknownType {
			var classDecl = symbol.ValueDeclaration
			if !c.isNodeWithinTypeDeclaration(node, classDecl) {
				c.error(node, diagnostic.M_0_is_not_an_enclosing_class,
					c.typeToString(resolveType, nil, true))
			}
		}
		needThisBeforeSuper = ast.IsConstructorDeclaration(container) && c.getDeclaredTypeOfSymbol(binder.GetSymbolOfNode(realContainer)) == resolveType

	} else {
		resolveType = c.getDeclaredTypeOfSymbol(c.getSymbolOfNode(realContainer))
		needThisBeforeSuper = ast.IsConstructorDeclaration(container)
	}

	if needThisBeforeSuper {
		c.checkThisBeforeSuper(node, container, diagnostic.M_super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class)
	}

	if ast.IsMethodDeclaration(container) {
		if ast.GetModifierFlags(container)&ast.MFStatic != 0 {
			c.error(node, diagnostic.M_this_cannot_be_referenced_from_a_static_context)
		}
	}

	return resolveType
}

func (c *checker) getSuperContainer(node ast.Node) ast.Node {
	for node != nil {
		node = node.Parent()
		if node == nil {
			return node
		}
		// !!!
		switch n := node.(type) {
		case *ast.ArrowExpression,
			*ast.FieldDeclaration,
			*ast.MethodDeclaration,
			*ast.ConstructorDeclaration:
			return n
		}
	}
	return nil
}

func (c *checker) findFirstSuperCall(n ast.Node) *ast.CallExpression {
	if ast.IsSuperCall(n) {
		return n.(*ast.CallExpression)
	} else if ast.IsFunctionLike(n) {
		return nil
	}

	var result *ast.CallExpression
	ast.ForEachChild(n, func(node ast.Node) bool {
		result = c.findFirstSuperCall(node)
		return result != nil
	}, nil)
	return result
}

// Return a cached result if super-statement is already found.
// Otherwise, find a super statement in a given constructor function and cache the result in the ast-links of the constructor
//
// param constructor constructor-function to look for super statement
func (c *checker) getSuperCallInConstructor(constructor *ast.ConstructorDeclaration) *ast.CallExpression {
	var links = c.getNodeLinks(constructor)

	// Only trying to find super-call if we haven't yet tried to find one.  Once we try, we will record the result
	if links.hasSuperCall == nil {
		links.superCall = c.findFirstSuperCall(constructor.Body)
		links.hasSuperCall = xbase.NewBool(links.superCall != nil)
	}
	return links.superCall
}

func (c *checker) checkSuperExpression(node *ast.SuperExpression) Type {
	var isCallExpression = ast.IsCallExpression(node.Parent()) && node.Parent().(*ast.CallExpression).Expression == node
	var container = ast.GetThisOrSuperContainer(node)

	if container == nil {
		c.error(node, diagnostic.M_super_cannot_be_referenced_in_current_location)
		return c.unknownType
	}

	if isCallExpression && node.Expression != nil {
		c.error(node.Expression, diagnostic.M_super_call_cannot_has_qualified_name)
		return c.unknownType
	}

	// Get the real containing object
	var realContainer = container
	if ast.IsConstructorDeclaration(container) || ast.IsMethodDeclaration(container) {
		realContainer = container.Parent()
	}

	var inStaticContext = false
	var inDefaultImplement = false
	// The'static' and'default' of the constructor are invalid, so this needs to be judged in the method.
	if ast.IsMethodDeclaration(container) {
		inStaticContext = ast.GetModifierFlags(container)&ast.MFStatic != 0
		inDefaultImplement = ast.IsInterfaceDeclaration(realContainer) || ast.IsAnnotationDeclaration(realContainer)
	}
	if ast.IsInterfaceLike(realContainer) {
		inStaticContext = true
	}

	var isLegalUsageOfSuperExpression = func() bool {
		if container == nil {
			return false
		}

		if isCallExpression {
			// Super calls are only permitted in constructors of derived classes
			return ast.IsConstructorDeclaration(container)
		} else {

			// Unqualified super reference is not allowed in extension method
			if inDefaultImplement {
				return false
			}

			// Super cannot be referenced from a static context
			if inStaticContext {
				return false
			}

			if ast.IsClassLike(realContainer) {
				return true
			}

			if ast.IsInterfaceLike(realContainer) && node.Expression != nil {
				return true
			}
		}

		return false
	}

	if !isLegalUsageOfSuperExpression() {
		if inStaticContext {
			c.error(node, diagnostic.M_super_cannot_be_referenced_from_a_static_context)
		} else if inDefaultImplement {
			c.error(node, diagnostic.M_Unqualified_super_reference_is_not_allowed_in_extension_method)
		} else if isCallExpression {
			c.error(node, diagnostic.M_Super_call_must_be_the_first_statement_in_the_constructor_when_parent_class_has_no_parameter_constructor_or_default_constructor)
		} else {
			c.error(node, diagnostic.M_super_cannot_be_referenced_in_current_location)
		}
	}

	// This cannot use super before
	if !isCallExpression && ast.IsClassLike(realContainer) && ast.IsConstructorDeclaration(container) {
		c.checkThisBeforeSuper(node, container, diagnostic.M_super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
	}

	var nodeCheckFlag nodeCheckFlags
	if isCallExpression {
		nodeCheckFlag = superStatic
	} else {
		nodeCheckFlag = superInstance
	}

	c.getNodeLinks(node).flags |= nodeCheckFlag


 	var superType Type
	var staticSuperType Type
	if node.Expression != nil {
		var symbol = c.resolveEntityName(node, node.Expression, binder.Class, node)
		if symbol == nil {
			c.error(node.Expression, diagnostic.M_Class_name_expected_here)
			return c.unknownType
		}

		superType = c.getDeclaredTypeOfSymbol(symbol)
		staticSuperType = c.getTypeOfSymbol(symbol)

		if superType != c.unknownType {
			var classDecl = symbol.ValueDeclaration
			if !c.isNodeWithinTypeDeclaration(node, classDecl) {
				c.error(node, diagnostic.M_0_is_not_an_enclosing_class,
					c.typeToString(superType, nil, true))
			}
		}

	} else {
		// Just class like can not expr
		switch realContainer.(type) {
		case *ast.ClassDeclaration:
			var tpe = c.getDeclaredTypeOfSymbol(binder.GetSymbolOfNode(realContainer))
			superType = c.getClassExtendsType(tpe.(InterfaceType))
			if superType == nil {
				superType = c.globalObjectType
			}
			staticSuperType = c.getTypeOfSymbol(superType.Symbol())
		case *ast.EnumDeclaration,
			*ast.BodyDeclaration:
			superType = c.globalObjectType
		default:
			panic("container kind is illegal.")
		}
	}

	if nodeCheckFlag == superStatic {
		return staticSuperType
	}
	return superType
}

// Return contextual type of parameter or undefined if no contextual type is available
func (c *checker) getContextuallyTypedParameterType(param *ast.ParameterDeclaration) Type {
	switch n := param.Parent().(type) {
	case *ast.ArrowExpression:
		if c.isContextSensitiveExpression(n) {
			var signature = c.getContextualSignature(n)
			if signature != nil {
				return c.getTypeAtPosition(signature, n.Declaration.Parameters.Index(param))
			}
		}
	}
	return nil
}

func (c *checker) getContextualTypeForInitializerExpression(node ast.Expression) Type {
	var varLike = node.Parent().(ast.VariableLike)
	if node == varLike.GetInitializer() {
		if varLike.GetType() != nil {
			return c.getTypeFromTypeNode(varLike.GetType())
		}
	}
	return nil
}

func (c *checker) getContextualTypeForReturnExpression(node ast.Expression) Type {
	var fun = ast.GetContainingFunction(node)
	if fun != nil {
		var result = fun.(ast.FunctionLike).GetType()
		// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
		// corresponding set accessor has a type annotation, return statements in the function are contextually typed
		if result != nil || ast.IsConstructorDeclaration(fun) {
			return c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(fun))
		}
		// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
		// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
		var signature = c.getContextualSignatureForFunctionLikeDeclaration(fun)
		if signature != nil {
			return c.getReturnTypeOfSignature(signature)
		}
	}
	return nil
}

func (c *checker) getContextualTypeForArgument(expr ast.Expression, arg ast.Expression) Type {
	var index = -1
	switch n := expr.(type) {
	case *ast.CallExpression:
		index = n.Arguments.Index(arg)
	case *ast.ConstructorExpression:
		index = n.Arguments.Index(arg)
	default:
		panic("getContextualTypeForArgument arg(expr) must is call expr or new expr.")
	}

	if index >= 0 {
		var signature = c.getResolvedOrAnySignature(expr)
		return c.getTypeAtPosition(signature, index)
	}
	return nil
}

func (c *checker) getContextualTypeForNewArrayExpression(expr *ast.NewArrayExpression, node ast.Node) Type {
	if expr.Initializer == node {
		return c.getTypeFromTypeNode(expr.Type)
	}

	return nil
}

func (c *checker) getContextualTypeForBinaryOperand(node ast.Expression) Type {
	var binaryExpression = node.Parent().(*ast.BinaryExpression)
	var operator = binaryExpression.Operator
	if operator.Token.IsAssignmentOperator() {
		if node == binaryExpression.Right {
			return c.checkExpression(binaryExpression.Left, nil)
		}
	}
	return nil
}

func (c *checker) getContextualTypeForElementExpression(node ast.Expression) Type {
	var arrayLiteral = node.Parent().(*ast.ArrayLiteralExpression)
	var tpe = c.getContextualType(arrayLiteral)
	if tpe != nil && tpe.Flags()&TFObjectLike != 0 &&
		c.getTargetType(tpe) == c.globalArrayType {
		return c.getArrayElementType(tpe)
	}
	return nil
}

func (c *checker) getContextualTypeForConditionalOperand(node ast.Expression) Type {
	var conditional = node.Parent().(*ast.ConditionalExpression)
	if node == conditional.WhenTrue || node == conditional.WhenFalse {
		return c.getContextualType(conditional)
	}
	return nil
}

func (c *checker) getContextualType(node ast.Expression) Type {
	if node == nil {
		return nil
	}

	var contextualType = c.getNodeLinks(node).contextualType
	if contextualType != nil {
		return contextualType
	}

	var parent = node.Parent()
	switch pn := parent.(type) {
	case *ast.VariableDeclaration,
		*ast.FieldDeclaration:
		return c.getContextualTypeForInitializerExpression(node)
	case *ast.ArrowExpression,
		*ast.ReturnStatement:
		return c.getContextualTypeForReturnExpression(node)
	case *ast.CallExpression:
		return c.getContextualTypeForArgument(pn, node)
	case *ast.NewObjectExpression:
		return c.getContextualTypeForArgument(pn, node)
	case *ast.NewArrayExpression:
		return c.getContextualTypeForNewArrayExpression(pn, node)
	case *ast.AsExpression:
		return c.getTypeFromTypeNode(pn.Type)
	case *ast.BinaryExpression:
		return c.getContextualTypeForBinaryOperand(node)
	case *ast.ArrayLiteralExpression:
		return c.getContextualTypeForElementExpression(node)
	case *ast.ConditionalExpression:
		return c.getContextualTypeForConditionalOperand(node)
	case *ast.ParenthesizedExpression:
		return c.GetContextualType(pn)
	}
	return nil
}

func (c *checker) getContextualSignatureForFunctionLikeDeclaration(node ast.Declaration) *Signature {
	// Only function expressions, arrow functions, and object literal methods are contextually typed.
	switch n := node.(type) {
	case *ast.ArrowFunctionDeclaration:
		var arrow = n.Parent().(*ast.ArrowExpression)
		return c.getContextualSignature(arrow)
	}
	return nil
}

func (c *checker) hasFunctionInterfaceAnnotation(node ast.Node) bool {
	var annotations = node.GetAnnotations()
	if annotations != nil {
		for i := 0; i < annotations.Len(); i ++ {
			var annotation = annotations.At(i).(*ast.Annotation)
			var tpe = c.getTypeFromTypeNode(annotation.Type)
			if tpe == c.globalFunctionInterfaceType {
				return true
			}
		}
	}
	return false
}

func (c *checker) isOverrideToObject(name string, source *Signature) bool {
	var symbol = c.getMethodOfType(c.globalObjectType, name)
	for _, target := range c.getSignaturesOfSymbol(symbol) {
		if c.isSignatureOverrideTo(source, target) {
			return true
		}
	}

	return false
}

func (c *checker) isTypeFromArrowFunction(tpe Type) bool {
	return tpe.Symbol() != nil && tpe.Symbol().Flags&binder.Function != 0
}

func (c *checker) isFunctionalInterface(tpe Type) bool {
	if c.getTargetType(tpe).Flags()&TFInterface == 0 {
		return false
	}

	var target = c.getTargetSymbol(tpe.Symbol())
	var links = c.getSymbolLinks(target)
	if links.isFunctionalInterface == nil {
		var resolvedName string
		var resolvedSignature *Signature

		for true {
			var declaration = tpe.Symbol().ValueDeclaration
			if !c.hasFunctionInterfaceAnnotation(declaration) {
				break
			}

		outer:
			for _, method := range c.getMethodsOfType(tpe) {
				var name = method.Name
				var signatures = c.getInheritedSignaturesOfSymbol(method)
				for _, signature := range signatures {
					if !c.isAbstractSignature(signature) {
						continue
					}

					if c.isOverrideToObject(name, signature) {
						continue
					}

					if resolvedSignature == nil {
						resolvedSignature = signature
						resolvedName = name
						continue
					}

					resolvedSignature = nil
					break outer
				}
			}
			break
		}

		if resolvedSignature == nil {
			links.isFunctionalInterface = xbase.NewBool(false)
		} else {
			links.isFunctionalInterface = xbase.NewBool(true)
			links.methodName = xbase.NewString(resolvedName)
		}
	}
	return *links.isFunctionalInterface
}

func (c *checker) getFunctionInterfaceSignature(tpe Type) *Signature {
	if !c.isFunctionalInterface(tpe) {
		panic("getFunctionInterfaceSignature argument(type) must is functional interface")
	}

	var target = c.getTargetSymbol(tpe.Symbol())
	var links = c.getSymbolLinks(target)
	var methodSymbol = c.getMethodOfType(tpe, *links.methodName)
	var anonymousType = c.getTypeOfSymbol(methodSymbol)
	var signatures = c.getSignaturesOfType(anonymousType, SIKCall)
	return signatures[0]
}

func (c *checker) getContextualSignature(node ast.Expression) *Signature {
	var tpe = c.GetContextualType(node)
	if tpe != nil && c.isFunctionalInterface(tpe){
		return c.getFunctionInterfaceSignature(tpe)
	}
	return nil
}

// Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is
// used as a special marker for other purposes.
func (c *checker) isInferentialContext(mapper *TypeMapper) bool {
	return mapper != nil && mapper != c.identityMapper
}

func (c *checker) checkArrayLiteral(node *ast.ArrayLiteralExpression, contextualMapper *TypeMapper) Type {
	var elementTypes []Type
	for i := 0; i < node.Elements.Len(); i ++ {
		var element = node.Elements.At(i)
		if !ast.IsOmittedExpression(element) {
			var tpe = c.checkExpression(element, contextualMapper)
			if !containsType(elementTypes, tpe) {
				elementTypes = append(elementTypes, tpe)
			}
		}
	}

	var contextualType Type = nil
	if !c.isInferentialContext(contextualMapper) {
		contextualType = c.getContextualType(node)
	}

	var contextualElementType Type
	if contextualType != nil && c.getTargetType(contextualType) == c.globalArrayType {
		contextualElementType = c.getArrayElementType(contextualType)
	}

	var bestCommonType = c.getBestCommonType(elementTypes, contextualElementType, true)
	if bestCommonType != nil {
		return c.createArrayType(bestCommonType)
	}

	c.error(node, diagnostic.M_Cannot_infer_suitable_type)
	return c.unknownType
}

func (c *checker) getDeclarationModifierFlagsFromSymbol(symbol *binder.Symbol) ast.ModifierFlags {
	if symbol != nil && !isNull(c.resolveSymbol(symbol).ValueDeclaration) {
		return c.getDeclarationModifierFlagsFromDeclaration(symbol.ValueDeclaration)
	}

	return 0
}

const resolvedModifierFlagsCache = "ResolvedModifierFlagsCache"
func (c *checker) getDeclarationModifierFlagsFromDeclaration(node ast.Declaration) ast.ModifierFlags {
	if !isNull(node) {
		if node.Get(resolvedModifierFlagsCache) != nil {
			return node.Get(resolvedModifierFlagsCache).(ast.ModifierFlags)
		}

		var flags = ast.GetCombinedModifierFlags(node)
		// Top level type declaration (class/interface/enum/annotation)
		if ast.IsTopLevelDeclaration(node) {
			if ast.IsClassLike(node) {
				// Filter allowed modifiers
				flags = flags&(ast.MFPublic|ast.MFAbstract|ast.MFFinal)
				// Top level type declaration default is static
				flags |= ast.MFStatic
				// Abstract and final cannot coexist
				if flags&ast.MFAbstract != 0 {
					flags &= ^ast.MFFinal
				}

			} else if ast.IsInterfaceLike(node) {
				// Filter allowed modifiers
				flags = flags&(ast.MFPublic)
				// Top level type declaration default is static
				flags |= ast.MFStatic
			} else {
				panic("top level type declaration must is class like or interface like.")
			}
		} else {

			// Deal with local variables and parameters
			if ast.IsVariableDeclaration(node) || ast.IsParameterDeclaration(node) {
				flags = flags&(ast.MFFinal)
			} else {

				// Handling type member in other cases
				if ast.IsClassLike(node.Parent()) {
					switch {
					case ast.IsConstructorDeclaration(node):
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected)
						if flags&ast.MFAccessibilityModifier == 0 {
							flags |= ast.MFFriend
						}
					case ast.IsFieldDeclaration(node):
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected|ast.MFStatic|ast.MFFinal|ast.MFTransient|ast.MFVolatile)
						if flags&ast.MFAccessibilityModifier == 0 {
							flags |= ast.MFFriend
						}
					case ast.IsMethodDeclaration(node):
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected|ast.MFAbstract|ast.MFStatic|ast.MFSynchronized)
						if flags&ast.MFAccessibilityModifier == 0 {
							flags |= ast.MFFriend
						}
					case ast.IsClassLike(node):
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected|ast.MFFinal|ast.MFStatic|ast.MFAbstract)
						if flags&ast.MFAccessibilityModifier == 0 {
							flags |= ast.MFFriend
						}
						if flags&ast.MFAbstract != 0 {
							flags &= ^ast.MFFinal
						}

					case ast.IsInterfaceLike(node):
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected)
						if flags&ast.MFAccessibilityModifier == 0 {
							flags |= ast.MFFriend
						}
					default:
						panic("Unknown class member")
					}
				} else if ast.IsInterfaceLike(node.Parent()) {
					switch {
					case ast.IsFieldDeclaration(node):
						flags |= ast.MFPublic
						flags = flags&(ast.MFPublic|ast.MFStatic)
					case ast.IsMethodDeclaration(node):
						flags |= ast.MFAbstract
						flags |= ast.MFPublic
						flags = flags&(ast.MFPublic|ast.MFStatic|ast.MFAbstract|ast.MFDefault|ast.MFFinal)
						if flags&ast.MFDefault != 0 {
							flags &= ^ast.MFAbstract
						}
						if flags&ast.MFStatic != 0 {
							flags &= ^(ast.MFAbstract|ast.MFDefault)
						}
					case ast.IsClassLike(node):
						flags |= ast.MFStatic
						flags |= ast.MFPublic
						flags = flags&(ast.MFPublic|ast.MFPrivate|ast.MFProtected|ast.MFFinal|ast.MFStatic|ast.MFAbstract)
						if flags&ast.MFAbstract != 0 {
							flags &= ^ast.MFFinal
						}

					case ast.IsInterfaceLike(node):
						flags |= ast.MFPublic
						flags = flags&(ast.MFPublic)
					default:
						panic("Unknown class member")
					}
				} else {
					flags = 0
				}
			}
		}

		node.Set(resolvedModifierFlagsCache, flags)
		return flags
	}

	return 0
}

func (c *checker) checkMemberAccessibility(node ast.Node, left ast.Node, member *binder.Symbol, reportError bool) bool {
	if member.Flags&binder.MethodLike != 0 {
		panic("If you want to check the accessibility of the method, please use 'checkSignatureAccessibility'")
	}

	var flags = c.getDeclarationModifierFlagsFromSymbol(member)
	var errorNode ast.Node
	switch n := node.(type) {
	case *ast.SelectorExpression:
		errorNode = n.Name
	case *ast.VariableDeclaration:
		errorNode = n.Name
	case *ast.QualifiedName:
		errorNode = n.Right
	case *ast.InstanceAccessExpression:
		errorNode = n.Right
	default:
		errorNode = n
	}

	var error = func(message *diagnostic.Message, args ...interface{}) {
		if reportError {
			c.error(errorNode, message, args...)
		}
	}

	// Public member are otherwise accessible.
	if flags&(ast.MFPrivate|ast.MFFriend|ast.MFProtected) == 0 {
		return true
	}

	// Member is known to be private or protected or internal at this point
	// If ast in class internal, private or protected or default is accessible.
	var typeDeclaration = c.getTypeDeclarationOfSymbol(c.GetParentOfSymbol(member))
	if c.isNodeWithinTypeDeclaration(node, typeDeclaration) {
		return true
	}

	// Private member is accessible if the property is within the declaring class
	if flags&ast.MFPrivate != 0 {
		error(diagnostic.M_0_is_private_and_only_accessible_within_class_1,
			c.symbolToString(member, nil, 0, true),
			c.typeToString(c.getDeclaringTypeDeclaration(member), nil, true))
		return false
	}

	// All protected member of a supertype are accessible in a super access
	if ast.IsSuperExpression(left) {
		return true
	}

	// member is accessible if the member is within the same package
	if ast.GetPackageOfNode(node) == ast.GetPackageOfNode(member.ValueDeclaration) {
		return true
	}
	if flags&ast.MFFriend != 0 {
		error(diagnostic.M_0_not_public_and_only_accessible_on_same_package,
			c.symbolToString(member, nil, 0, true))
		return false
	}

	// Member is known to be protected at this point

	// Find the first enclosing class that has the declaring classes of the protected constituents
	// of the property as base classes
	var enclosingType Type
	var memberDeclaringType = c.getDeclaringTypeDeclaration(member)
	c.forEachEnclosingTypeDeclaration(node, func(enclosingDeclaration ast.Node, inStatic bool) bool {
		enclosingType = c.getDeclaredTypeOfSymbol(c.getSymbolOfNode(enclosingDeclaration)).(InterfaceType)
		if !c.hasBaseType(enclosingType, memberDeclaringType) {
			enclosingType = nil
		}

		return enclosingType != nil
	})

	if isNull(enclosingType) {
		error(diagnostic.M_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
			c.symbolToString(member, nil, 0, true),
			c.typeToString(memberDeclaringType, nil, true))
		return false
	}
	return true
}

func (c *checker) checkSignatureAccessibility(node ast.Node, left ast.Node, member *binder.Symbol, signature *Signature, reportError bool) bool {
	var flags = c.getDeclarationModifierFlagsFromDeclaration(signature.Declaration)
	var errorNode ast.Node
	switch n := node.(type) {
	case *ast.SelectorExpression:
		errorNode = n.Name
	case *ast.VariableDeclaration:
		errorNode = n.Name
	case *ast.QualifiedName:
		errorNode = n.Right
	case *ast.InstanceAccessExpression:
		errorNode = n.Right
	default:
		errorNode = n
	}

	var error = func(message *diagnostic.Message, args ...interface{}) {
		if reportError {
			c.error(errorNode, message, args...)
		}
	}

	if ast.IsSuperExpression(left) {
		// TS 1.0 spec (April 2014): 4.8.2
		// - In a constructor, instance member function, instance member accessor, or
		//   instance member variable initializer where this references a derived class instance,
		//   a super property access is permitted and must specify a public instance member function of the base class.
		// - In a static member function or static member accessor
		//   where this references the constructor function object of a derived class,
		//   a super property access is permitted and must specify a public static member function of the base class.

		if flags&ast.MFAbstract != 0 {
			// A method cannot be accessed in a super property access if the method is abstract.
			// This error could mask a private property access error. But, a member
			// cannot simultaneously be private and abstract, so this will trigger an
			// additional error elsewhere.
			var functionName = c.symbolToString(member, node, binder.MethodLike, true)
			error(diagnostic.M_Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression,
				c.signatureToString(functionName, signature, node, true),
				c.typeToString(c.getDeclaringTypeDeclaration(member), nil, true))
			return false
		}
	}

	// Public member are otherwise accessible.
	if flags&(ast.MFPrivate|ast.MFFriend|ast.MFProtected) == 0 {
		return true
	}

	// Member is known to be private or protected or internal at this point
	// If ast in class internal, private or protected or default is accessible.
	var typeDeclaration = c.getTypeDeclarationOfSymbol(c.GetParentOfSymbol(member))
	if c.isNodeWithinTypeDeclaration(node, typeDeclaration) {
		return true
	}

	// Private member is accessible if the property is within the declaring class
	if flags&ast.MFPrivate != 0 {
		var functionName = c.symbolToString(member, node, binder.MethodLike, true)
		error(diagnostic.M_0_is_private_and_only_accessible_within_class_1,
			c.signatureToString(functionName, signature, node, true),
			c.typeToString(c.getDeclaringTypeDeclaration(member), nil, true))
		return false
	}

	// All protected member of a supertype are accessible in a super access
	if ast.IsSuperExpression(left) {
		return true
	}

	// member is accessible if the member is within the same package
	if ast.GetPackageOfNode(node) == ast.GetPackageOfNode(member.Declarations[0]) {
		return true
	}
	if flags&ast.MFFriend != 0 {
		var functionName = c.symbolToString(member, node, binder.MethodLike, true)
		error(diagnostic.M_0_not_public_and_only_accessible_on_same_package,
			c.signatureToString(functionName, signature, node, true))
		return false
	}

	// Member is known to be protected at this point

	// Find the first enclosing class that has the declaring classes of the protected constituents
	// of the property as base classes
	var enclosingType Type
	var memberDeclaringType = c.getDeclaringTypeDeclaration(member)
	c.forEachEnclosingTypeDeclaration(node, func(enclosingDeclaration ast.Node, inStatic bool) bool {
		enclosingType = c.getDeclaredTypeOfSymbol(c.getSymbolOfNode(enclosingDeclaration)).(InterfaceType)
		if !c.hasBaseType(enclosingType, memberDeclaringType) {
			enclosingType = nil
		}

		return enclosingType != nil
	})

	if isNull(enclosingType) {
		var functionName = c.symbolToString(member, node, binder.MethodLike, true)
		error(diagnostic.M_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
			c.signatureToString(functionName, signature, node, true),
			c.typeToString(memberDeclaringType, nil, true))
		return false
	}
	return true
}

func (c *checker) checkNonNullExpression(node ast.Expression) Type {
	return c.checkNonNullType(c.checkExpression(node, nil), node)
}

func (c *checker) checkNonNullType(tpe Type, errorNode ast.Node) Type {
	if tpe.Flags()&TFNullable != 0 {
		c.error(errorNode, diagnostic.M_Object_is_possibly_null)
	}
	return tpe
}

func (c *checker) checkClassExpression(node *ast.ClassExpression) Type {
	var tpe = c.getTypeFromNode(node.Type)
	if tpe == c.unknownType {
		return c.unknownType
	}

	return c.createClassType(tpe)
}

func (c *checker) checkQualifiedName(node *ast.QualifiedName) Type {
	var leftType = c.checkNonNullExpression(node.Left)
	if c.isTypeAny(leftType) {
		return leftType
	}

	return c.checkSelectorAccessExpressionOrQualifiedName(node, node.Left, leftType, node.Right)
}

func (c *checker) checkSelectorExpression(node *ast.SelectorExpression) Type {
	var leftType = c.checkNonNullExpression(node.Expression)
	if c.isTypeAny(leftType) {
		return leftType
	}

	return c.checkSelectorAccessExpressionOrQualifiedName(node, node.Expression, leftType, node.Name)
}

func (c *checker) checkInstanceAccessExpression(node *ast.InstanceAccessExpression) Type {
	var leftType = c.checkNonNullExpression(node.Left)
	if c.isTypeAny(leftType) {
		return leftType
	}

	// Instance access can access the constructor
	if node.Right.OriginalToken == token.ConstructorKeyword {
		c.getNodeLinks(node).resolvedSymbol = leftType.Symbol()
		return leftType
	}

	var leftSymbol = leftType.Symbol()
	if leftSymbol == nil || leftSymbol.Flags&(binder.ClassLike|binder.InterfaceLike) == 0 {
		c.error(node.Left, diagnostic.M_The_left_side_of_the_instance_access_must_be_a_class_or_interface)
		return c.unknownType
	}
	leftType = c.getDeclaredTypeOfSymbol(leftSymbol)

	return c.checkSelectorAccessExpressionOrQualifiedName(node, node.Left, leftType, node.Right)
}

func (c *checker) checkSelectorAccessExpressionOrQualifiedName(node ast.Expression, left ast.Expression, leftType Type, right *ast.Identifier) Type {
	var apparentType = c.getApparentType(leftType)
	if apparentType == c.unknownType || leftType.Flags()&TFTypeParameter != 0 && c.isTypeAny(apparentType) {
		// handle cases when type is Type parameter with invalid or any constraint
		return apparentType
	}

	var symbol *binder.Symbol
	if apparentType.Flags()&TFPackage == 0 {
		if ast.IsCallTarget(node) {
			symbol = c.getMethodOfType(apparentType, right.Value)
		} else {
			symbol = c.getFieldOfType(apparentType, right.Value)
		}
	} else {
		// Apparent type is package type, so we need find type members.
		symbol = c.getMemberOfType(apparentType, right.Value, binder.Type)
	}

	// If left type is package type, we can find in package.
	if symbol == nil && leftType.Flags()&TFPackage != 0 && ast.IsSelectorExpression(node.Parent()) {
		var path = c.getFullEntityName(node)
		symbol = c.getPackageSymbol(node, path)
	}

	if symbol == nil {
		if len(right.Value) > 0 {
			if ast.IsCallTarget(node) {
				c.error(right, diagnostic.M_Method_0_does_not_exist_on_type_1,
					ast.DeclarationNameToString(right),
					c.typeToString(leftType, nil, true))
			} else {
				c.error(right, diagnostic.M_Field_0_does_not_exist_on_type_1,
					ast.DeclarationNameToString(right),
					c.typeToString(leftType, nil, true))
			}
		}
		return c.unknownType
	}

	c.markMemberAsReferenced(symbol)
	c.getNodeLinks(node).resolvedSymbol = symbol

	// If left type is package type, so symbol may be is top level symbol.
	if leftType.Flags()&TFPackage != 0 {
		c.checkTopLevelSymbolAccessibility(right, symbol, true)
	} else {
		if symbol.Flags&binder.MethodLike != 0 {
			c.getNodeLinks(node).accessibilityDefferCheckHandle = func(signature *Signature) bool {
				return c.checkSignatureAccessibility(node, left, symbol, signature, true)
			}
		} else {
			c.checkMemberAccessibility(node, left, symbol, true)
		}
	}

	var memberType = c.getTypeOfSymbol(symbol)

	var assignmentKind = ast.GetAssignmentTargetKind(node)
	if assignmentKind != ast.NoAssign {
		if c.isReadOnlySymbol(symbol) {
			c.error(right, diagnostic.M_Cannot_assign_to_0_because_it_is_a_read_only, right.Value)
			return c.unknownType
		}
	}

	// Only compute control flow type if this is a property access expression that isn't an
	// assignment target, and the referenced property was declared as a variable, property,
	// accessor, or optional method.
	if ast.IsSelectorExpression(node) ||
		assignmentKind == ast.Definite ||
		symbol.Flags&(binder.Variable|binder.Field) == 0 {
		return memberType
	}
	c.getNodeLinks(node).resolvedFlowType = c.getFlowTypeOfReference(node, memberType, true, nil)
	return memberType
}

func (c *checker) reportNonexistentProperty(propNode *ast.Identifier, containingType Type) {
	var errorInfo *diagnostic.MessageChain
	errorInfo = diagnostic.ChainDiagnosticMessages(errorInfo, diagnostic.M_Field_0_does_not_exist_on_type_1,
		ast.DeclarationNameToString(propNode), c.typeToString(containingType, nil, true))
	c.diagnostics = append(c.diagnostics, utilities.CreateDiagnosticForNodeFromMessageChain(propNode, errorInfo))
}

func (c *checker) reportNonexistentMethod(propNode *ast.Identifier, containingType Type) {
	var errorInfo *diagnostic.MessageChain
	errorInfo = diagnostic.ChainDiagnosticMessages(errorInfo, diagnostic.M_Field_0_does_not_exist_on_type_1,
		ast.DeclarationNameToString(propNode), c.typeToString(containingType, nil, true))
	c.diagnostics = append(c.diagnostics, utilities.CreateDiagnosticForNodeFromMessageChain(propNode, errorInfo))
}

func (c *checker) markMemberAsReferenced(member *binder.Symbol) {
	if member != nil &&
		c.noUnusedIdentifiers &&
		member.Flags&binder.ClassMember != 0 &&
		member.ValueDeclaration != nil && ast.GetModifierFlags(member.ValueDeclaration)&ast.MFPrivate != 0 {
		if c.getCheckFlags(member)&instantiated != 0 {
			c.getSymbolLinks(member).target.IsReferenced = true
		} else {
			member.IsReferenced = true
		}
	}
}

func (c *checker) checkIndexedAccess(node *ast.ElementAccessExpression) Type {
	var objectType = c.checkNonNullExpression(node.Expression)

	var indexExpression = node.ArgumentExpression
	var argType = c.checkExpression(indexExpression, nil)
	c.checkTypeAssignableTo(argType, c.intType, nil, indexExpression, nil)

	if objectType == c.unknownType {
		return objectType
	}

	if c.isArrayType(objectType) {
		return c.getArrayElementType(objectType)
	}

	// Report error
	return c.unknownType
}

func (c *checker) isCheckedExceptions(tpe Type) bool {
	return c.isTypeSubtypeOf(tpe, c.globalThrowableType) &&
		!c.isTypeSubtypeOf(tpe, c.globalRuntimeExceptionType) &&
		!c.isTypeSubtypeOf(tpe, c.globalErrorType)
}

func (c *checker) checkExceptionIsCaptured(node ast.Node, throw Type) bool {
	if !c.isCheckedExceptions(throw) {
		return false
	}

	var captured = false
	var location = node.Parent()
	outer:
	for location != nil {
		switch n := location.(type) {
		case *ast.ConstructorDeclaration,
			*ast.MethodDeclaration:
			var curSignature = c.getSignatureFromDeclaration(n.(ast.FunctionLike))
			for _, curThrow := range curSignature.Throws {
				if c.isTypeSubtypeOf(throw, curThrow) {
					captured = true
				}
			}
		case *ast.TryStatement:
			for i := 0; i < n.CatchClauses.Len(); i ++ {
				var clause = n.CatchClauses.At(i).(*ast.CatchClause)
				if clause.VariableDeclaration.Type != nil {
					var capturedType = c.getTypeFromTypeNode(clause.VariableDeclaration.Type)
					if c.isTypeSubtypeOf(throw, capturedType) {
						captured = true
						break
					}
				}
			}

		case *ast.ClassDeclaration,
			*ast.BodyDeclaration,
			*ast.EnumDeclaration,
			*ast.InterfaceDeclaration,
			*ast.AnnotationDeclaration:
			break outer
		}

		location = location.Parent()
	}

	if !captured {
		// Unhandled exception: java.io.IOException
		c.error(node, diagnostic.M_Unhandled_exception_0,
			c.typeToString(throw, nil, true))
	}
	return captured
}

func (c *checker) checkSignatureThrowsIsCaptured(node ast.Node, signature *Signature){
	for _, throw := range signature.Throws {
		c.checkExceptionIsCaptured(node, throw)
	}
}



func (c *checker) getNumberConstantValue(expr ast.Expression) *Number {
	var flowType = c.getNodeLinks(expr).resolvedFlowType
	if isNull(flowType) || flowType.Flags()&TFNumber == 0 {
		return nil
	}

	if ast.IsConstantExpression(expr) {
		return flowType.(*numberLiteralType).value
	}

	return nil
}

func (c *checker) resolveUntypedCall(callParam callParameter) *Signature {
	if callParam.Arguments != nil {
		for i := 0; i < callParam.Arguments.Len(); i ++ {
			var arg = callParam.Arguments.At(i)
			c.checkExpression(arg, nil)
		}
	}
	return c.anySignature
}

func (c *checker) resolveErrorCall(callParam callParameter) *Signature {
	c.resolveUntypedCall(callParam)
	return c.unknownSignature
}

func (c *checker) isCandidateSignature(callParam callParameter, signature *Signature) bool {
	var args = callParam.Arguments
	return (signature.HasRestParameter || args == nil || args.Len() <= len(signature.Parameters)) &&
		(callParam.TypeArguments == nil || signature.TypeParameters != nil && callParam.TypeArguments.Len() == len(signature.TypeParameters))
}

func (c *checker) collectCandidates(callParam callParameter, signatures []*Signature) []*Signature {
	var result []*Signature
	for _, signature := range signatures {
		if c.isCandidateSignature(callParam, signature) {
			result = append(result, signature)
		}
	}
	return result
}

// If type has a single call signature and no other members, return that signature. Otherwise, return undefined.
func (c *checker) getSingleCallSignature(tpe Type) *Signature {
	if tpe.Flags()&TFObjectLike != 0 {
		var resolved = c.resolveObjectTypeMembers(tpe.(ObjectType))
		if len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 &&
			len(resolved.Fields()) == 0 {
			return resolved.CallSignatures()[0]
		}
	}
	return nil
}

// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
func (c *checker) instantiateSignatureInContextOf(signature *Signature, contextualSignature *Signature, contextualMapper *TypeMapper) *Signature {
	var context = c.createInferenceContext(signature.TypeParameters)
	c.forEachMatchingParameterType(contextualSignature, signature, func(source Type, target Type) {
		// Type parameters from outer context referenced by session type are fixed by instantiation of the session type
		context.inferTypes(c.instantiateType(source, contextualMapper), target)
	})
	return c.getSignatureInstantiation(signature, context.getInferredTypes())
}

type Tristate = int

const (
	TriNull = iota
	TriTrue
	TriFalse
)

func (c *checker) inferTypeArguments(signature *Signature, args *ast.ExpressionList, excludeArgument []Tristate) []Type {
	var typeParameters = signature.TypeParameters
	var context = c.createInferenceContext(typeParameters)
	var mapper = c.createInferenceMapper(context)
	// First infer from arguments that are not context sensitive
	for i := 0; i < args.Len(); i++ {
		if excludeArgument == nil || excludeArgument[i] == TriNull {
			var parameterType = c.getTypeAtPosition(signature, i)
			context.inferTypes(c.checkExpressionWithContextualType(args.At(i), parameterType, mapper), parameterType)
		}
	}
	// Next, infer from those context sensitive arguments that are no longer excluded
	if excludeArgument != nil {
		for i := 0; i < args.Len(); i++ {
			if excludeArgument[i] == TriFalse {
				var parameterType = c.getTypeAtPosition(signature, i)
				context.inferTypes(c.checkExpressionWithContextualType(args.At(i), parameterType, mapper), parameterType)
			}
		}
	}
	return context.getInferredTypes()
}

func (c *checker) checkTypeArguments(signature *Signature, typeArguments *ast.TypeList) []Type {
	var typeParameters = signature.TypeParameters
	var result []Type
	for i := 0; i < len(typeParameters); i++ {
		var typeArgNode = typeArguments.At(i)
		var typeArgument = c.getTypeFromTypeNode(typeArgNode)
		var constraint = c.getConstraintOfTypeParameter(typeParameters[i])
		if c.produceDiagnostics {
			var apparentType = c.getApparentType(typeArgument)
			if isNull(constraint) || constraint == c.noConstraintType {
				c.checkTypeSubtypeOf(apparentType, c.globalObjectType, typeArgNode, diagnostic.M_Type_0_does_not_satisfy_the_constraint_1)
			} else {
				c.checkTypeSubtypeOf(apparentType, constraint, typeArgNode, diagnostic.M_Type_0_does_not_satisfy_the_constraint_1)
			}
		}

		result = append(result, typeArgument)
	}
	return result
}

type signatureMatchInfo struct {
	match bool
	count int
	difference int
	containAny bool
	signature *Signature
}

// When the matching degree is greater than or equal to 0, it is a match, otherwise it is not a match.
// The greater the absolute value, the farther the difference.
func (c *checker) checkApplicableSignature(callParam callParameter, signature *Signature,
	relation *relatedCache, excludeArgument []Tristate, errorNode ast.Node, reportErrors bool) (info signatureMatchInfo) {
	info.signature = signature
	info.match = true
	for i := 0; i < callParam.Arguments.Len(); i ++ {
		var argument = callParam.Arguments.At(i)
		var paramType = c.getTypeAtPosition(signature, i)
		var mapper *TypeMapper
		if excludeArgument != nil && excludeArgument[i] != TriTrue {
			mapper = c.identityMapper
		}
		var argType = c.checkExpressionWithContextualType(argument, paramType, mapper)

		// Use argument expression as error location when reporting errors
		var errNode ast.Node
		if reportErrors {
			errNode = argument
		}

		var diff = c.checkTypeRelatedTo(argType, paramType, c.getNumberConstantValue(argument), relation, errNode,
			diagnostic.M_Argument_of_type_0_is_not_assignable_to_parameter_of_type_1)
		if diff != noMatch {
			info.count ++
			info.difference += diff
		} else {
			info.match = false
		}
		info.containAny = info.containAny || (c.isTypeAny(paramType)||c.isTypeAny(argType))
	}

	if reportErrors {
		if callParam.Arguments.Len() < signature.MinArgumentCount {
			c.error(errorNode, diagnostic.M_Too_few_Argument)
		}
	}

	info.match = info.match && callParam.Arguments.Len() >= len(signature.Parameters)
	return info
}

// When the matching degree is greater than or equal to 0, it is a match, otherwise it is not a match.
// The greater the absolute value, the farther the difference.
func (c *checker) checkCodeLocationApplicableSignature(arguments *ast.DeclarationList, signature *Signature,
	relation *relatedCache, errorNode ast.Node, reportErrors bool) (info signatureMatchInfo) {
	info.signature = signature
	info.match = true
	for i := 0; i < arguments.Len(); i ++ {
		var docType = arguments.At(i).(*ast.DocumentTypeDeclaration)
		var argType = c.getTypeFromTypeNode(docType.Type)
		var paramType = c.getTypeAtPosition(signature, i)

		// Use argument expression as error location when reporting errors
		var errNode ast.Node
		if reportErrors {
			errNode = arguments.At(i)
		}

		var diff = c.checkTypeRelatedTo(argType, paramType, nil, relation, errNode,
			diagnostic.M_Argument_of_type_0_is_not_assignable_to_parameter_of_type_1)
		if diff != noMatch {
			info.count ++
			info.difference += diff
		} else {
			info.match = false
		}
	}

	if reportErrors {
		if arguments.Len() < signature.MinArgumentCount {
			c.error(errorNode, diagnostic.M_Too_few_Argument)
		}
	}

	info.match = info.match && arguments.Len() >= len(signature.Parameters)
	return info
}

func intIndexOf(arr []int, item int) int {
	for i, val := range arr {
		if val == item {
			return i
		}
	}
	return -1
}

func (c *checker) resolveCall(callParam callParameter, signatures []*Signature, errorNode ast.Node,
	callback func(signature *Signature, fullMatch bool)) (*Signature, []*Signature) {
	for _, typeArg := range callParam.TypeArguments.Array() {
		c.checkSourceElement(typeArg)
	}

	var candidates = c.collectCandidates(callParam, signatures)
	if len(candidates) == 0 {
		c.error(errorNode, diagnostic.M_Supplied_parameters_do_not_match_any_signature_of_call_target)
		return  c.resolveUntypedCall(callParam), nil
	}

	var args = new(ast.ExpressionList)
	if callParam.Arguments != nil {
		args = callParam.Arguments
	}
	var excludeArgument []Tristate
	for i := 0; i < args.Len(); i++ {
		if c.isContextSensitiveExpression(args.At(i)) {
			if excludeArgument == nil {
				excludeArgument = make([]Tristate, args.Len())
			}
			excludeArgument[i] = TriTrue
		}
	}

	var relation = c.assignableRelation
	var candidate *Signature
	var matchSignatureInfos []signatureMatchInfo
	// Choose Overload
	for i := 0; i < len(candidates); i++ {
		for true {
			candidate = candidates[i]
			if !isNull(candidate.TypeParameters) {
				var typeArguments []Type
				if callParam.TypeArguments.Len() > 0 {
					typeArguments = c.checkTypeArguments(candidate, callParam.TypeArguments)
				} else {
					typeArguments = c.inferTypeArguments(candidate, args, excludeArgument)
				}

				candidate = c.getSignatureInstantiation(candidate, c.getBoxingTypes(typeArguments))
			}
			matchInfo := c.checkApplicableSignature(callParam, candidate,
				relation, excludeArgument, nil, false)
			if !matchInfo.match {
				matchSignatureInfos = append(matchSignatureInfos, matchInfo)
				break
			}
			var index = -1
			if len(excludeArgument) > 0 {
				index = intIndexOf(excludeArgument, TriTrue)
			}
			if index < 0 {
				matchSignatureInfos = append(matchSignatureInfos, matchInfo)
				break
			}
			excludeArgument[index] = TriFalse
		}
	}

	resolvedSignatureInfo, resolvedSignatures := c.chooseOverload(errorNode, matchSignatureInfos, func(signature *Signature, fullMatch bool) {
		// No signatures were applicable. Now report errors based on the last applicable signature with
		// no arguments excluded from assignability checks. and reset the type for context-sensitive parameters.
		c.checkApplicableSignature(callParam, signature, relation, nil, errorNode, true)
		// Full match and then further check for errors
		if fullMatch {
			c.checkSignatureThrowsIsCaptured(errorNode, signature)
		}
		// Call the upper callback, which may require accessibility detection or static context detection
		callback(signature, fullMatch)
	})
	return resolvedSignatureInfo, resolvedSignatures
}

func (c *checker) chooseOverload(errorNode ast.Node, matchSignatureInfos []signatureMatchInfo, callback func(signature *Signature, fullMath bool)) (*Signature, []*Signature) {
	// The higher the match, the higher the front
	sort.Slice(matchSignatureInfos, func(i, j int) bool {
		if matchSignatureInfos[i].match && !matchSignatureInfos[j].match {
			return true
		}
		if matchSignatureInfos[i].match == matchSignatureInfos[j].match {
			if matchSignatureInfos[i].count > matchSignatureInfos[j].count {
				return true
			}
			if matchSignatureInfos[i].difference < matchSignatureInfos[j].difference {
				return true
			}
		}
		return false
	})
	var resolvedSignatures []*Signature
	for _, info := range matchSignatureInfos {
		resolvedSignatures = append(resolvedSignatures, info.signature)
	}

	var ambiguous []*Signature
	// Ambiguous overload checker
	for i:=1; i< len(matchSignatureInfos); i++ {
		var first = matchSignatureInfos[0]
		var info = matchSignatureInfos[i]
		if  first.match &&
			first.count == info.count &&
			first.difference == info.difference &&
			first.match == info.match{
			ambiguous = append(ambiguous, info.signature)
		}
	}

	if len(ambiguous) > 0 && matchSignatureInfos[0].match &&
		!matchSignatureInfos[0].containAny {
		c.error(errorNode, diagnostic.M_Ambiguous_overload_selection)
	}

	var fullMatch = len(ambiguous) == 0 && matchSignatureInfos[0].match &&
		!matchSignatureInfos[0].containAny
	callback(matchSignatureInfos[0].signature, fullMatch)

	return matchSignatureInfos[0].signature, resolvedSignatures
}

func (c *checker) resolveCallExpression(node *ast.CallExpression) (*Signature, []*Signature) {
	var callParam = callParameter{
		TypeArguments: node.TypeArguments,
		Arguments:     node.Arguments,
	}

	if ast.IsSuperExpression(node.Expression) {
		var superType = c.checkSuperExpression(node.Expression.(*ast.SuperExpression))
		if superType != c.unknownType {
			return c.resolveCall(callParam, c.getSignaturesOfType(superType, SIKConstruct), node, func(signature *Signature, fullMatch bool) {
				// We need to check the accessibility of super's constructor, and after a full match
				if fullMatch {
					if c.getDeclarationModifierFlagsFromDeclaration(signature.Declaration)&ast.MFPrivate != 0 &&
						!c.isNodeWithinTypeDeclaration(node.Expression, superType.Symbol().ValueDeclaration) {
						// If applied in a static context, an error has been reported during'checkSuperExpression'
						c.error(node.Expression, diagnostic.M_0_is_private_and_only_accessible_within_class_1,
							c.signatureToString(token.ConstructorKeyword.ToString(), signature, node.Expression, true),
							c.symbolToString(superType.Symbol(), node.Expression, binder.Value, true))
					}
				}
			})
		}
		return c.resolveUntypedCall(callParam), nil
	}

	var funcType = c.checkExpression(node.Expression, nil)
	if funcType == c.unknownType {
		// Another error has already been reported
		return c.resolveErrorCall(callParam), nil
	}

	var apparentType = c.getApparentType(funcType)
	var signatures []*Signature
	if apparentType != c.unknownType {
		signatures = append(signatures, c.getSignaturesOfType(apparentType, SIKCall)...)
	}

	if len(signatures) == 0 {
		c.error(node, diagnostic.M_Cannot_invoke_an_expression_whose_type_lacks_a_call_signature)
		return c.resolveErrorCall(callParam), nil
	}

	return c.resolveCall(callParam, signatures, node, func(signature *Signature, fullMatch bool) {
		if fullMatch {
			// Check referencing a non-static context in a static context
			if c.isNodeToSymbolViaStaticContext(node.Expression, funcType.Symbol()) &&
				c.getDeclarationModifierFlagsFromDeclaration(signature.Declaration)&ast.MFStatic == 0{
				var symbol = funcType.Symbol()
				c.error(node.Expression, diagnostic.M_Non_static_0_cannot_be_referenced_from_a_static_context,
					c.signatureToString(c.symbolToString(symbol, node.Expression, 0, true),
						signature, node.Expression, true))
			}

			// We need to check the accessibility of super's constructor, and after a full match
			if fullMatch && c.getNodeLinks(node.Expression).accessibilityDefferCheckHandle != nil {
				c.getNodeLinks(node.Expression).accessibilityDefferCheckHandle(signature)
				c.getNodeLinks(node.Expression).accessibilityDefferCheckHandle = nil
			}
		}
	})
}

func (c *checker) getNestedClassType(scope ast.Expression, typeName ast.Node) Type {
	var callerType = c.checkExpression(scope, nil)
	if callerType == c.unknownType {
		return c.unknownType
	}

	if !ast.IsIdentifier(typeName) {
		c.error(typeName, diagnostic.M_Invalid_qualified_new)
		return c.unknownType
	}

	var name = typeName.(*ast.Identifier).Value
	var classSymbol = c.getMemberOfType(callerType, name, binder.Type)
	if classSymbol == nil {
		c.error(typeName, diagnostic.M_Cannot_find_name_0, name)
		// Another error has already been reported
		return c.unknownType
	}

	c.checkMemberAccessibility(typeName, typeName, classSymbol, true)
	c.getNodeLinks(typeName).resolvedSymbol = classSymbol
	return c.getTypeOfSymbol(classSymbol)
}

func (c *checker) resolveConstructSignature(errorNode ast.Node, tpe Type, callParam callParameter,
	callback func(signature *Signature, fullMatch bool)) (*Signature, []*Signature) {
	// If ConstructExpr's apparent type(section 3.8.1) is an object type with one or
	// more construct signatures, the expression is processed in the same manner as a
	// function  call, but using the construct signatures as the initial set of candidate
	// signatures for overload resolution.The result type of the function  call becomes
	// the result type of the operation.
	tpe = c.getApparentType(tpe)
	if tpe == c.unknownType {
		// handler cases when original declType is a type parameter with invalid constraint
		// another error has already been reported
		return c.resolveErrorCall(callParam), nil
	}

	// If the expression is a class of abstract type, then it cannot be instantiated.
	// Note, only class declarations can be declared abstract.
	// In the case of a merged class-module or class-interface declaration,
	// only the class declaration node will have the Abstract flag set.
	if c.getDeclarationModifierFlagsFromDeclaration(tpe.Symbol().ValueDeclaration)&ast.MFAbstract != 0 {
		c.error(errorNode, diagnostic.M_Cannot_create_an_instance_of_the_abstract_class_0,
			c.symbolToString(tpe.Symbol(), errorNode, binder.Value, true))
		return c.resolveErrorCall(callParam), nil
	}

	// Technically, this signatures list may be incomplete. We are taking the apparent type,
	// but we are not including construct signatures that may have been added to the Expr or
	// Function interface, since they have none by default. This is a bit of a leap of faith
	// that the user will not add any.
	var constructSignatures = c.getSignaturesOfType(tpe, SIKConstruct)
	if len(constructSignatures) == 0 {
		c.error(errorNode, diagnostic.M_Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature)
		return c.resolveErrorCall(callParam), nil
	}

	return c.resolveCall(callParam, constructSignatures, errorNode, callback)
}

func (c *checker) resolveConstructExpression(node *ast.ConstructorExpression) (*Signature, []*Signature) {
	var callParam = callParameter{
		TypeArguments: node.TypeArguments,
		Arguments:     node.Arguments,
	}

	var tpe Type
	if isNull(node.Scope) {
		var symbol = c.resolveEntityName(node, node.TypeName, binder.Type, node)
		if symbol == nil {
			// Another error has already been reported
			return c.resolveErrorCall(callParam), nil
		}
		if symbol.Flags&binder.InterfaceLike != 0 {
			// Better error reporting
			c.grammarErrorAtPos(ast.GetSourceFileOfNode(node), node.Arguments.Pos(),
				node.Arguments.End() - node.Arguments.Pos(), diagnostic.M_New_interface_does_not_allow_a_parameter_list)
			return c.resolveErrorCall(callParam), nil
		}
		tpe = c.getTypeOfSymbol(symbol)
	} else {
		tpe = c.getNestedClassType(node.Scope, node.TypeName)
		if tpe == c.unknownType {
			// Another error has already been reported
			return c.resolveErrorCall(callParam), nil
		}
	}

	return c.resolveConstructSignature(node, tpe, callParam, func(signature *Signature, fullMatch bool) {
		if fullMatch {
			var constructorSymbol = tpe.Symbol().Members.GetSymbol(ast.ConstructorName, binder.Constructor)
			c.checkSignatureAccessibility(node.TypeName, node.Scope, constructorSymbol, signature, true)
		}
	})
}

func (c *checker) resolveEnumMember(node *ast.EnumItemDeclaration) (*Signature, []*Signature) {
	var parent = node.Parent().(*ast.EnumDeclaration)
	var tpe = c.getTypeOfSymbol(binder.GetSymbolOfNode(parent))

	var callParam = callParameter{
		TypeArguments: nil,
		Arguments:     node.Arguments,
	}
	return c.resolveConstructSignature(node, tpe, callParam, func(signature *Signature, fullMatch bool) {
		// Nothing to do
	})
}

func (c *checker) resolveDocumentCallExpression(node *ast.DocumentCallExpression) (*Signature, []*Signature) {
	var tpe = c.checkExpression(node.Expression, nil)
	if tpe == c.unknownType {
		// Another error has already been reported
		return c.unknownSignature, nil
	}

	var apparentType = c.getApparentType(tpe)
	var signatures []*Signature
	if apparentType != c.unknownType {
		var isConstructorCall = ast.IsInstanceAccessExpression(node.Expression) &&
			node.Expression.(*ast.InstanceAccessExpression).Right.OriginalToken == token.ConstructorKeyword
		if isConstructorCall {
			signatures = append(signatures, c.getSignaturesOfType(apparentType, SIKConstruct)...)
		} else {
			signatures = append(signatures, c.getSignaturesOfType(apparentType, SIKCall)...)
		}
	}

	if len(signatures) == 0 {
		c.error(node, diagnostic.M_Cannot_invoke_an_expression_whose_type_lacks_a_call_signature)
		return c.unknownSignature, nil
	}

	var matchSignatureInfos []signatureMatchInfo
	for _, signature := range signatures {
		if !(signature.HasRestParameter || node.Arguments == nil || node.Arguments.Len() <= len(signature.Parameters)) {
			continue
		}

		var matchInfo = c.checkCodeLocationApplicableSignature(node.Arguments,
			signature, c.assignableRelation, nil,false)
		matchSignatureInfos = append(matchSignatureInfos, matchInfo)
	}
	if len(matchSignatureInfos) == 0 {
		c.error(node, diagnostic.M_Supplied_parameters_do_not_match_any_signature_of_call_target)
		return c.unknownSignature, nil
	}

	resolvedSignature, resolvedSignatures := c.chooseOverload(node.Expression, matchSignatureInfos, func(signature *Signature, fullMath bool) {
		// TODO: implement it
	})
	// No signatures were applicable. Now report errors based on the last applicable signature with
	// no arguments excluded from assignability checks. and reset the type for context-sensitive parameters.
	c.checkCodeLocationApplicableSignature(node.Arguments, resolvedSignature,
		c.assignableRelation, node.Expression, true)
	return resolvedSignature, resolvedSignatures
}

func (c *checker) resolveSignature(node ast.Node) (*Signature, []*Signature) {
	switch n := node.(type) {
	case *ast.CallExpression:
		return c.resolveCallExpression(n)
	case *ast.ConstructorExpression:
		return c.resolveConstructExpression(n)
	case *ast.EnumItemDeclaration:
		return c.resolveEnumMember(n)
	case *ast.DocumentCallExpression:
		return c.resolveDocumentCallExpression(n)
	default:
		panic("Branch in 'resolveSignature' should be unreachable.")
	}
}

func (c *checker) getResolvedSignature(node ast.Node) (*Signature, []*Signature) {
	var links = c.getNodeLinks(node)
	var cache = links.resolvedSignature
	if cache != nil && cache != c.resolvingSignature {
		return cache, links.resolvedCandidates
	}

	links.resolvedSignature = c.resolvingSignature
	signature, candidates := c.resolveSignature(node)

	// If signature resolution originated in control flow type analysis (for example to compute the
	// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
	// types from the control flow analysis.
	if c.flowLoopStart == c.flowLoopCount {
		links.resolvedSignature = signature
	} else {
		links.resolvedSignature = cache
	}
	return signature, candidates
}

func (c *checker) getResolvedSignatureOnly(node ast.Node) *Signature {
	resolved, _ := c.getResolvedSignature(node)
	return resolved
}

func (c *checker) getResolvedOrAnySignature(node ast.Node) *Signature {
	// If we're already in the process of resolving the given signature, don't resolve again as
	// that could cause infinite recursion. Instead, return anySignature.
	if c.getNodeLinks(node).resolvedSignature == c.resolvingSignature {
		return c.resolvingSignature
	}
	return c.getResolvedSignatureOnly(node)
}

func (c *checker) checkAssertion(node *ast.AsExpression) Type {
	var exprType = c.checkExpression(node.Expression, nil)

	c.checkSourceElement(node.Type)
	var targetType = c.getTypeFromTypeNode(node.Type)
	if c.produceDiagnostics && targetType != c.unknownType {
		if !c.areTypesComparable(exprType, targetType) {
			c.error(node.Type, diagnostic.M_Type_0_cannot_be_converted_to_type_1,
				c.typeToString(exprType, node, true),
				c.typeToString(targetType, node, true))
		}
	}
	return targetType
}

func (c *checker) checkDocumentCallExpression(node *ast.DocumentCallExpression) Type {
	var signature = c.getResolvedSignatureOnly(node)
	if ast.IsSuperExpression(node.Expression) {
		return c.voidType
	}
	return c.getReturnTypeOfSignature(signature)
}

func (c *checker) checkCallExpression(node *ast.CallExpression) Type {
	var signature = c.getResolvedSignatureOnly(node)
	if ast.IsSuperExpression(node.Expression) {
		return c.voidType
	}
	return c.getReturnTypeOfSignature(signature)
}

func (c *checker) checkQualifiedExpression(node *ast.QualifiedExpression) Type {
	var tpe = c.getTypeFromTypeNode(node.Type)
	if tpe == c.unknownType {
		return c.unknownType
	}

	// Provide better error reporting
	if tpe.Flags()&TFClassLike != 0 {
		c.grammarErrorAtPos(ast.GetSourceFileOfNode(node), node.Type.End(), 1, diagnostic.M_0_expected, "(")
	} else if tpe.Flags()&TFInterfaceLike == 0 {
		c.error(node.Type, diagnostic.M_Expected_class_or_interface)
	}

	return tpe
}

func (c *checker) checkConstructExpression(node *ast.ConstructorExpression) Type {
	var signature = c.getResolvedSignatureOnly(node)
	return c.getReturnTypeOfSignature(signature)
}

func (c *checker) checkNewObjectExpression(node *ast.NewObjectExpression) Type {
	c.checkGrammarNewObjectExpression(node)
	if node.Body == nil {
		return c.checkExpression(node.Type, nil)
	} else {
		c.checkAnonymousClassDeclaration(node.Body)
		var symbol = binder.GetSymbolOfNode(node.Body)
		return c.getDeclaredTypeOfSymbol(symbol)
	}
}

func (c *checker) checkNewArrayExpression(node *ast.NewArrayExpression) Type {
	c.checkGrammarNewArrayExpression(node)
	var tpe = c.getTypeFromTypeNode(node.Type)
	if node.Initializer != nil {
		var initType = c.checkExpression(node.Initializer, nil)
		c.checkTypeAssignableTo(initType, tpe, nil, node.Initializer, nil)
	}
	return tpe
}

func (c *checker) checkParenthesizedExpression(node *ast.ParenthesizedExpression, contextualMapper *TypeMapper) Type {
	var tpe = c.checkExpression(node.Expression, contextualMapper)
	c.getNodeLinks(node).resolvedFlowType = c.getNodeLinks(node.Expression).resolvedFlowType
	return tpe
}

func (c *checker) getTypeAtPosition(signature *Signature, pos int) Type {
	if signature.HasRestParameter {
		if pos < len(signature.Parameters)-1 {
			return c.getTypeOfSymbol(signature.Parameters[pos])
		} else {
			return c.getRestTypeOfSignature(signature)
		}
	} else {
		if pos < len(signature.Parameters) {
			return c.getTypeOfSymbol(signature.Parameters[pos])
		} else {
			return c.unknownType
		}
	}
}

func (c *checker) assignContextualParameterTypes(signature *Signature, context *Signature, mapper *TypeMapper) {
	var restLen int
	if signature.HasRestParameter {
		restLen = 1
	}
	var length = len(signature.Parameters) - restLen
	for i := 0; i < length; i++ {
		var parameter = signature.Parameters[i]
		if parameter.ValueDeclaration.(*ast.ParameterDeclaration).Type == nil {
			var links = c.getSymbolLinks(parameter)
			links.tpe = c.instantiateType(c.getTypeAtPosition(context, i), mapper)
		}
	}
	if signature.HasRestParameter && context.HasRestParameter && len(signature.Parameters) >= len(context.Parameters) {
		var parameter = signature.Parameters[len(signature.Parameters)-1]
		if parameter.ValueDeclaration.(*ast.ParameterDeclaration).Type == nil {
			var links = c.getSymbolLinks(parameter)
			links.tpe = c.instantiateType(c.getTypeOfSymbol(context.Parameters[len(context.Parameters)-1]), mapper)
		}
	}
}

func (c *checker) getReturnTypeFromBody(fun ast.Declaration, contextualMapper *TypeMapper) Type {
	if !ast.IsFunctionLike(fun) {
		panic("getReturnTypeFromBody input decl not function like")
	}

	var body = ast.GetFunctionLikeBody(fun)
	if body == nil {
		return c.unknownType
	}

	var tpe Type
	if !ast.IsBlockStatement(body) {
		tpe = c.checkExpressionCached(body.(ast.Expression), contextualMapper)
	} else {
		// Aggregate the types_ of expressions within all the return statements.
		var types_ = c.checkAndAggregateReturnExpressionTypes(body.(*ast.BlockStatement), contextualMapper)

		// Try to return the best common type if we have any return expressions.
		if len(types_) > 0 {
			tpe = c.getBestCommonType(types_, nil, true)
			if tpe == nil {
				c.error(fun, diagnostic.M_No_best_common_type_exists_among_return_expressions)
				return c.unknownType
			}
		}
	}

	if tpe != nil {
		return tpe
	}

	return c.voidType
}

// WARNING: This has the same semantics as the forEach family of functions,
//          in that traversal terminates in the event that 'visitor' supplies a truthy value.
func (c *checker) forEachReturnStatement(node ast.Node, visitor func(stmt *ast.ReturnStatement) bool) bool {
	switch n := node.(type) {
	case *ast.ReturnStatement:
		return visitor(n)
	case *ast.BlockStatement,
		*ast.IfStatement,
		*ast.DoStatement,
		*ast.WhileStatement,
		*ast.ForStatement,
		*ast.ForOfStatement,
		*ast.SwitchStatement,
		*ast.CaseClause,
		*ast.LabeledStatement,
		*ast.TryStatement:
		return ast.ForEachChild(node, func(node ast.Node) bool {
			return c.forEachReturnStatement(node, visitor)
		}, nil)
	}
	return false
}

// Returns a set of types relating to every return expression relating to a func (checker *checker)  block.
func (c *checker) checkAndAggregateReturnExpressionTypes(body *ast.BlockStatement, contextualMapper *TypeMapper) []Type {
	var aggregatedTypes []Type
	c.forEachReturnStatement(body, func(returnStmt *ast.ReturnStatement) bool {
		var expr = returnStmt.Expression
		if expr != nil {
			var tpe = c.checkExpressionCached(expr, contextualMapper)
			if !containsType(aggregatedTypes, tpe) {
				aggregatedTypes = append(aggregatedTypes, tpe)
			}
		}
		// Return false, keep foreach
		return false
	})

	return aggregatedTypes
}

func (c *checker) functionHasImplicitReturn(fun ast.Declaration) bool {
	if fun.Flags()&ast.HasImplicitReturn == 0 {
		return false
	}
	return true
}

/**
 * TypeScript Specification 1.0 (6.3) - July 2014
 *   An explicitly typed function whose return type isn't the Void type,
 *   the Any type, or a union type containing the Void or Any type as a constituent
 *   must have at least one return statement somewhere in its body.
 *   An exception to this rule is if the function implementation consists of a single 'throw' statement.
 *
 * @param returnType - return type of the function, can be undefined if return type is not explicitly specified
 */
func (c *checker) checkAllCodePathsInNonVoidFunctionReturnOrThrow(fun ast.FunctionLike, returnType Type) {
	if !c.produceDiagnostics {
		return
	}

	// Functions that return 'void' don't need any return expressions.
	if returnType == c.voidType {
		return
	}

	var body = ast.GetFunctionLikeBody(fun)
	// If all we have is a function signature, or an arrow function  with an expression body, then there is nothing to check.
	if  ast.NodeIsMissing(body) || !ast.IsBlockStatement(body) || !c.functionHasImplicitReturn(fun)  {
		return
	}

	var hasExplicitReturn = fun.Flags()&ast.HasExplicitReturn != 0
	if returnType != nil && !hasExplicitReturn {
		// minimal check: function has syntactic return type annotation and no explicit return statements in the body
		// this function does not conform to the specification.
		// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
		c.error(fun.GetType(), diagnostic.M_Missing_return_statement)
		return
	}

	if returnType == nil {
		// If return type annotation is omitted check if function has any explicit return statements.
		// If it does not have any - its inferred return type is void - don't do any checks.
		// Otherwise get inferred return type from function body and report error only if it is not void / anytype
		if !hasExplicitReturn {
			return
		}
		var inferredReturnType = c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(fun))
		if inferredReturnType == c.voidType {
			return
		}
	}

	var errorNode ast.Node = fun
	if fun.GetType() != nil {
		errorNode = fun.GetType()
	}
	c.error(errorNode, diagnostic.M_Not_all_code_paths_return_a_value)
}

func (c *checker) checkArrowExpression(node *ast.ArrowExpression, contextualMapper *TypeMapper) Type {
	// The identityMapper object is used to indicate that function expressions are wildcards
	if contextualMapper == c.identityMapper {
		return c.anyFunctionType
	}

	var declaration = node.Declaration
	var links = c.getNodeLinks(node)
	var tpe = c.getTypeOfSymbol(binder.GetSymbolOfNode(declaration))
	var contextSensitive = c.isContextSensitiveExpression(node)
	var mightReset = contextSensitive && c.isInferentialContext(contextualMapper) ||
		contextSensitive && c.getNodeLinks(node).contextualType != nil

	// Check if function expression is contextually typed and assign parameter types if so.
	// See the comment in assignTypeToParameterAndFixTypeParameters to understand why we need to
	// check mightReset.
	if mightReset || links.flags&contextChecked == 0 {
		var contextualSignature = c.getContextualSignature(node)
		// If a type check is started at a function expression that is an argument of a function  call, obtaining the
		// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
		// already assigned contextual types.
		var _contextChecked = links.flags&contextChecked != 0
		if mightReset || !_contextChecked {
			links.flags |= contextChecked
			if contextualSignature != nil {
				var signature = c.getSignaturesOfType(tpe, SIKCall)[0]
				if contextSensitive {
					var mapper *TypeMapper
					if contextualMapper != nil {
						mapper = contextualMapper
					} else {
						mapper = c.identityMapper
					}
					c.assignContextualParameterTypes(signature, contextualSignature, mapper)
				}
				if mightReset || declaration.Type == nil && signature.ResolvedReturnType == nil {
					var returnType = c.getReturnTypeFromBody(declaration, contextualMapper)
					if signature.ResolvedReturnType == nil {
						signature.ResolvedReturnType = returnType
					}
				}
			}

			if !_contextChecked {
				c.checkSignatureDeclaration(declaration)
				c.checkNodeDeferred(node.Declaration)
			}
		}
	}

	return tpe
}

func (c *checker) checkArrowFunctionDeferred(declaration *ast.ArrowFunctionDeclaration) {
	if declaration.Body != nil {
		if declaration.Type == nil {
			// There are some checks that are only performed in getReturnTypeFromBody, that may produce errors
			// we need. An example is the noImplicitAny errors resulting from widening the return expression
			// of a function. Because checking of function expression bodies is deferred, there was never an
			// appropriate time to do this during the main walk of the file (see the comment at the top of
			// checkFunctionExpressionBodies). So it must be done now.
			c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(declaration))
		}

		if ast.IsBlockStatement(declaration.Body) {
			c.checkSourceElement(declaration.Body)
		} else {
			var expr = declaration.Body.(ast.Expression)
			var exprType = c.checkExpression(expr, nil)
			if declaration.Type != nil {
				c.checkTypeAssignableTo(exprType, c.getTypeFromTypeNode(declaration.Type),
					c.getNumberConstantValue(expr), declaration.Body, nil)
			}
		}
		c.registerForUnusedIdentifiersCheck(declaration)
	}
}

func (c *checker) checkArithmeticOperandType(operand ast.Node, tp Type, diagnostic *diagnostic.Message) bool {
	if tp.Flags()&(TFAny|TFNumberLike) == 0 {
		c.error(operand, diagnostic)
		return false
	}
	return true
}

func (c *checker) isReadOnlySymbol(symbol *binder.Symbol) bool {
	// The following symbols are considered read-only:
	// Properties with a 'final' modifier
	// Parameter with a 'final' modifier
	// Variable with a 'final' modifier
	// Enum members
	// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
	return c.getCheckFlags(symbol)&readOnly != 0 ||
		symbol.Flags&binder.Field != 0 && c.getDeclarationModifierFlagsFromSymbol(symbol)&ast.MFFinal != 0 ||
		symbol.Flags&binder.Variable != 0 && c.getDeclarationModifierFlagsFromSymbol(symbol)&ast.MFFinal != 0 ||
		symbol.Flags&binder.EnumMember != 0
}

func (c *checker) checkReferenceExpression(expr ast.Expression, invalidReferenceMessage *diagnostic.Message) bool {
	// References are combinations of identifiers, parentheses, and property accesses.
	var node = ast.SkipParenthesizedExpression(expr)
	switch node.(type) {
	case *ast.Identifier,
		*ast.SelectorExpression,
		*ast.ElementAccessExpression:
		return true
	}

	c.error(expr, invalidReferenceMessage)
	return false
}

func (c *checker) trySetUnaryExpressionFlowType(node ast.Node, tpe Type, operator token.Token) {
	if isNull(tpe) || !c.isLiteralType(tpe) {
		return
	}

	if tpe.Flags()&TFNumber == 0 {
		return
	}

	var numberType = tpe.(*numberLiteralType)
	var returnValue = c.calculateUnaryNumberConstantValue(numberType.value, operator)
	c.getNodeLinks(node).resolvedFlowType = c.getNumberLiteralTypeForText(returnValue.String(), returnValue.float)
}

func (c *checker) calculateUnaryNumberConstantValue(value *Number, operator token.Token) *Number {
	switch operator {
	case token.Plus:
		return value
	case token.Minus:
		var minusOneType = c.getLiteralMinusOneType()
		return value.Mul(minusOneType.value)
	case token.Tilde:
		return value.Not()
	case token.PlusPlus:
		var oneType = c.getLiteralOneType()
		return value.Add(oneType.value)
	case token.MinusMinus:
		var oneType = c.getLiteralOneType()
		return value.Sub(oneType.value)
	}

	panic("Unknown operator in 'calculateNumberConstantValue'")
}

func (c *checker) unaryNumericPromotionType(tpe Type) Type {
	if !c.isPrimitiveNumeric(tpe) {
		panic(fmt.Sprintf("Unary numeric promotion not possible on non-numeric-primitive type (%T)", tpe))
	}

	if tpe == c.doubleType {
		return c.doubleType
	} else if tpe == c.floatType {
		return c.floatType
	} else if tpe == c.longType {
		return c.longType
	} else {
		return c.intType
	}
}

func (c *checker) checkPreUnaryExpr(node *ast.PrefixUnaryExpression) Type {
	var operandType = c.checkExpression(node.Operand, nil)
	var unBoxingOperandType = c.unBoxingType(operandType)

	switch node.Operator.Token {
	case token.Plus, // +
		token.Minus, // -
		token.Tilde: // ~
		if unBoxingOperandType.Flags()&TFNumberLike == 0 {
			c.error(node, diagnostic.M_Operator_0_cannot_be_applied_to_1,
				node.Operator.Token.ToString(), c.typeToString(operandType, nil, true))
			return c.unknownType
		}
		c.trySetUnaryExpressionFlowType(node, c.getNodeLinks(node.Operand).resolvedFlowType, node.Operator.Token)
		return c.unaryNumericPromotionType(unBoxingOperandType)
	case token.Exclamation: // !
		if unBoxingOperandType != c.booleanType {
			c.error(node, diagnostic.M_Operator_0_cannot_be_applied_to_1,
				node.Operator.Token.ToString(), c.typeToString(operandType, nil, true))
			return c.unknownType
		}
		c.trySetUnaryExpressionFlowType(node, c.getNodeLinks(node.Operand).resolvedFlowType, node.Operator.Token)
		return c.booleanType
	case token.PlusPlus, // ++
		token.MinusMinus: // --
		if unBoxingOperandType.Flags()&TFNumberLike == 0 {
			c.error(node, diagnostic.M_Operator_0_cannot_be_applied_to_1,
				node.Operator.Token.ToString(), c.typeToString(operandType, nil, true))
			return c.unknownType
		} else {
			c.checkReferenceExpression(node.Operand, diagnostic.M_The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer)
			c.trySetUnaryExpressionFlowType(node, c.getNodeLinks(node.Operand).resolvedFlowType, node.Operator.Token)
		}
		return operandType
	}
	return c.unknownType
}

func (c *checker) checkPostUnaryExpr(node *ast.PostfixUnaryExpression) Type {
	var operandType = c.checkExpression(node.Operand, nil)
	var unBoxingOperandType = c.unBoxingType(operandType)
	if unBoxingOperandType.Flags()&TFNumberLike == 0 {
		c.error(node, diagnostic.M_Operator_0_cannot_be_applied_to_1,
			node.Operator.Token.ToString(), c.typeToString(operandType, nil, true))
	} else {
		c.getNodeLinks(node).resolvedFlowType = c.getNodeLinks(node.Operand).resolvedFlowType
		c.checkReferenceExpression(node.Operand, diagnostic.M_The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_property_or_indexer)
	}
	return operandType
}

func (c *checker) checkInstanceOfExpr(node *ast.BinaryExpression, leftType Type, rightType Type) Type {
	if !c.isTypeSubtypeOf(leftType, rightType) ||
		!c.isTypeSubtypeOf(rightType, leftType) {
		c.error(node, diagnostic.M_Inconvertible_types_cannot_cast_0_to_1,
			c.typeToString(leftType, nil, true),
			c.typeToString(rightType, nil, true))
		return c.falseType
	}
	return c.booleanType
}

func (c *checker) trySetBinaryExpressionFlowType(node ast.Node, leftFlowType Type, rightFlowType Type, operator token.Token) {
	if !c.isLiteralType(leftFlowType) || !c.isLiteralType(rightFlowType) {
		return
	}

	if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFNumber != 0 {
		var leftNumberType = leftFlowType.(*numberLiteralType)
		var rightNumberType = rightFlowType.(*numberLiteralType)
		var returnValue = c.calculateBinaryNumberConstantValue(leftNumberType.value, rightNumberType.value, operator)
		c.getNodeLinks(node).resolvedFlowType = c.getNumberLiteralTypeForText(returnValue.String(), returnValue.float)
	}

	if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFString != 0 ||
		leftFlowType.Flags()&TFString != 0 && rightFlowType.Flags()&TFNumber != 0 ||
		leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFString != 0 {
		var leftLiteralType = leftFlowType.(LiteralType)
		var rightLiteralType = rightFlowType.(LiteralType)
		c.getNodeLinks(node).resolvedFlowType = c.getStringLiteralTypeForType(leftLiteralType.string() + rightLiteralType.string())
	}
}

func (c *checker) calculateBinaryNumberConstantValue(leftValue *Number, rightValue *Number, operator token.Token) *Number {
	switch operator {
	case token.Plus:
		return leftValue.Add(rightValue)
	case token.Minus:
		return leftValue.Sub(rightValue)
	case token.Asterisk:
		return leftValue.Mul(rightValue)
	case token.Slash:
		return leftValue.Div(rightValue)
	case token.Percent:
		return leftValue.Mod(rightValue)
	case token.PlusEquals:
		return leftValue.Add(rightValue)
	case token.MinusEquals:
		return leftValue.Sub(rightValue)
	case token.AsteriskEquals:
		return leftValue.Mul(rightValue)
	case token.SlashEquals:
		return leftValue.Div(rightValue)
	case token.PercentEquals:
		return leftValue.Mod(rightValue)
	case token.LessThanLessThan:
		return leftValue.Lsh(rightValue)
	case token.LessThanLessThanEquals:
		return leftValue.Lsh(rightValue)
	case token.GreaterThanGreaterThan:
		return leftValue.Rsh(rightValue)
	case token.GreaterThanGreaterThanEquals:
		return leftValue.Rsh(rightValue)
	case token.GreaterThanGreaterThanGreaterThan:
		return leftValue.URsh(rightValue)
	case token.GreaterThanGreaterThanGreaterThanEquals:
		return leftValue.URsh(rightValue)
	case token.Bar:
		return leftValue.Or(rightValue)
	case token.BarEquals:
		return leftValue.Or(rightValue)
	case token.Caret:
		return leftValue.Xor(rightValue)
	case token.CaretEquals:
		return leftValue.Xor(rightValue)
	case token.Ampersand:
		return leftValue.And(rightValue)
	case token.AmpersandEquals:
		return leftValue.And(rightValue)
	}

	panic("Unknown operator in 'calculateNumberConstantValue'")
}

func (c *checker) trySetBinaryConditionFlowType(node ast.Node, leftFlowType Type, rightFlowType Type, operator token.Token) {
	if !c.isLiteralType(leftFlowType) || !c.isLiteralType(rightFlowType) {
		return
	}

	switch operator {
	case token.EqualsEquals: // ==
		// TODO: implement it
	case token.ExclamationEquals: // !=
		// TODO: implement it
	case token.LessThan: // <
		if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFNumber != 0 {
			var leftNumberType = leftFlowType.(*numberLiteralType)
			var rightNumberType = rightFlowType.(*numberLiteralType)
			var cmp = leftNumberType.value.Cmp(rightNumberType.value)
			if cmp == -1 {
				c.getNodeLinks(node).resolvedFlowType = c.trueType
			} else {
				c.getNodeLinks(node).resolvedFlowType = c.falseType
			}
		}
	case token.GreaterThan: // >
		if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFNumber != 0 {
			var leftNumberType = leftFlowType.(*numberLiteralType)
			var rightNumberType = rightFlowType.(*numberLiteralType)
			var cmp = leftNumberType.value.Cmp(rightNumberType.value)
			if cmp == 1 {
				c.getNodeLinks(node).resolvedFlowType = c.trueType
			} else {
				c.getNodeLinks(node).resolvedFlowType = c.falseType
			}
		}
	case token.LessThanEquals: // <=
		if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFNumber != 0 {
			var leftNumberType = leftFlowType.(*numberLiteralType)
			var rightNumberType = rightFlowType.(*numberLiteralType)
			var cmp = leftNumberType.value.Cmp(rightNumberType.value)
			if cmp <= 0 {
				c.getNodeLinks(node).resolvedFlowType = c.trueType
			} else {
				c.getNodeLinks(node).resolvedFlowType = c.falseType
			}
		}
	case token.GreaterThanEquals: // >=
		if leftFlowType.Flags()&TFNumber != 0 && rightFlowType.Flags()&TFNumber != 0 {
			var leftNumberType = leftFlowType.(*numberLiteralType)
			var rightNumberType = rightFlowType.(*numberLiteralType)
			var cmp = leftNumberType.value.Cmp(rightNumberType.value)
			if cmp >= 0 {
				c.getNodeLinks(node).resolvedFlowType = c.trueType
			} else {
				c.getNodeLinks(node).resolvedFlowType = c.falseType
			}
		}
	default:
		panic("Unknown operator in 'trySetBinaryConditionFlowType'")
	}
}

func (c *checker) binaryNumericPromotionType(tpe1 Type, tpe2 Type) Type {
	if !c.isPrimitiveNumeric(tpe1) || !c.isPrimitiveNumeric(tpe2) {
		panic(fmt.Sprintf("Binary numeric promotion not possible on types %T and %T", tpe1, tpe2))
	}

	if tpe1 == c.doubleType || tpe2 == c.doubleType {
		return c.doubleType
	} else if tpe1 == c.floatType || tpe2 == c.floatType {
		return c.floatType
	} else if tpe1 == c.longType || tpe2 == c.longType {
		return c.longType
	} else {
		return c.intType
	}
}

func (c *checker) checkBinaryExpression(node *ast.BinaryExpression, contextualMapper *TypeMapper) Type {
	var operator = node.Operator
	var leftType = c.checkExpression(node.Left, contextualMapper)
	var rightType = c.checkExpression(node.Right, contextualMapper)

	var unBoxingLeftType = c.unBoxingType(leftType)
	var unBoxingRightType = c.unBoxingType(rightType)

	var checkAssignmentOperator = func(valueType Type) {
		if c.produceDiagnostics && operator.Token.IsAssignmentOperator() {
			// TypeScript 1.0 spec (April 2014): 4.17
			// An assignment of the form
			//    VarExpr = ValueExpr
			// requires VarExpr to be classified as a reference
			// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
			// and the type of the non - compound operation to be assignable to the type of VarExpr.
			var ok = c.checkReferenceExpression(node.Left, diagnostic.M_Invalid_left_hand_side_of_assignment_expression)
			// Use default messages
			if ok {
				// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
				if valueType != nil {
					c.checkTypeAssignableTo(valueType, leftType, c.getNumberConstantValue(node.Right), node.Left, nil)
				} else {
					c.error(node.Left, diagnostic.M_Type_0_is_not_assignable_to_type_1)
				}

			}
		}
	}

	var reportOperatorError = func() {
		c.error(node.Operator, diagnostic.M_Operator_0_cannot_be_applied_to_types_1_and_2,
			node.Operator.Token.ToString(),
			c.typeToString(leftType, nil, true),
			c.typeToString(rightType, nil, true))
	}

	switch operator.Token {
	case token.Plus,    // +
		token.Minus,    // -
		token.Asterisk, // *
		token.Slash,    // /
		token.Percent:  // %

		if operator.Token == token.Plus {
			// string + string = string
			// number + string = string
			// string + number = string
			switch {
			case leftType.Flags()&TFString != 0 && rightType.Flags()&TFString != 0,
				leftType.Flags()&TFIntrinsic != 0 && rightType.Flags()&TFString != 0,
				leftType.Flags()&TFString != 0 && rightType.Flags()&TFIntrinsic != 0:
				c.trySetBinaryExpressionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
					c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
				return c.globalStringType
			}
		}

		// % operator not support float or double
		if operator.Token == token.Percent &&
			(unBoxingLeftType.Flags()&(TFFloat|TFDouble) != 0 || unBoxingRightType.Flags()&(TFFloat|TFDouble) != 0){
			reportOperatorError()
			return c.unknownType
		}


		var resultType Type
		if unBoxingLeftType.Flags()&TFNumberLike != 0 && unBoxingRightType.Flags()&TFNumberLike != 0 {
			resultType = c.binaryNumericPromotionType(unBoxingLeftType, unBoxingRightType)
			c.trySetBinaryExpressionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
				c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
		}

		if resultType == nil {
			reportOperatorError()
			return c.unknownType
		}

		return resultType
	case token.PlusEquals, 	  // +=
		token.MinusEquals,    // -=
		token.AsteriskEquals, // *=
		token.SlashEquals,    // /=
		token.PercentEquals:  // %=

		if operator.Token&token.PercentEquals != 0 &&
			(unBoxingLeftType.Flags()&(TFFloat|TFDouble) != 0 || unBoxingRightType.Flags()&(TFFloat|TFDouble) != 0) {
			reportOperatorError()
			return c.unknownType
		}

		var resultType Type
		if operator.Token == token.PlusEquals && leftType.Flags()&TFString != 0 {
			// There are three special cases
			// string += number
			// string += string
			// string += object
			if unBoxingLeftType.Flags()&TFString != 0 && unBoxingRightType.Flags()&TFNumberLike != 0 ||
				unBoxingLeftType.Flags()&TFString != 0 && unBoxingRightType.Flags()&TFString != 0 ||
				unBoxingLeftType.Flags()&TFString != 0 && c.isTypeSubtypeOf(rightType, c.globalObjectType) ||
				c.isTypeSubtypeOf(rightType, c.globalObjectType) && unBoxingLeftType.Flags()&TFString != 0  {
				c.trySetBinaryExpressionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
					c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
				resultType = c.globalStringType
			}
		}

		if leftType.Flags()&TFNumberLike != 0 && rightType.Flags()&TFNumberLike != 0 {
			resultType = leftType
			c.trySetBinaryExpressionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
				c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
		}

		if resultType == nil {
			reportOperatorError()
			return c.unknownType
		}

		c.checkReferenceExpression(node.Left, diagnostic.M_Invalid_left_hand_side_of_assignment_expression)
		return resultType

	case token.LessThanLessThan, 					   // <<
		token.LessThanLessThanEquals,                  // <<=
		token.GreaterThanGreaterThan,                  // >>
		token.GreaterThanGreaterThanEquals,            // >>=
		token.GreaterThanGreaterThanGreaterThan,       // >>>
		token.GreaterThanGreaterThanGreaterThanEquals, // >>>=
		token.Bar,                                     // |
		token.BarEquals,                               // |=
		token.Caret,                                   // ^
		token.CaretEquals,                             // ^=
		token.Ampersand,                               // &
		token.AmpersandEquals:                         // &=
		// Left and right must is int like.
		if unBoxingLeftType.Flags()&(TFFloat|TFDouble) != 0 || unBoxingRightType.Flags()&(TFFloat|TFDouble) != 0 {
			reportOperatorError()
			return c.unknownType
		}

		var resultType Type
		if unBoxingLeftType.Flags()&TFNumberLike != 0 && unBoxingRightType.Flags()&TFNumberLike != 0 {
			resultType = unBoxingLeftType
			c.trySetBinaryExpressionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
				c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
		}


		if isNull(resultType) {
			reportOperatorError()
			return c.unknownType
		}

		if operator.Token == token.LessThanLessThan ||
			operator.Token == token.GreaterThanGreaterThan ||
			operator.Token == token.GreaterThanGreaterThanGreaterThan {
			return c.unaryNumericPromotionType(resultType)
		}

		return resultType

	case token.EqualsEquals, 			// ==
		token.ExclamationEquals,       	// !=
		token.LessThan,                	// <
		token.GreaterThan,             	// >
		token.LessThanEquals,          	// <=
		token.GreaterThanEquals:       	// >=
		if !c.areTypesComparable(unBoxingLeftType, unBoxingRightType) {
			reportOperatorError()
		} else {
			c.trySetBinaryConditionFlowType(node, c.getNodeLinks(node.Left).resolvedFlowType,
				c.getNodeLinks(node.Right).resolvedFlowType, operator.Token)
		}


		return c.booleanType
	case token.InstanceOfKeyword: 		// InstanceOf
		return c.checkInstanceOfExpr(node, leftType, rightType)
	case token.AmpersandAmpersand, 		// &&
		token.BarBar: 					// ||
		if leftType.Flags()&TFBoolean == 0 || rightType.Flags()&TFBoolean == 0 {
			reportOperatorError()
		} else {
			if leftType == c.trueType || rightType == c.trueType {
				c.getNodeLinks(node).resolvedFlowType = c.trueType
				return c.booleanType
			} else if leftType == c.falseType && rightType == c.falseType {
				c.getNodeLinks(node).resolvedFlowType = c.falseType
				return c.booleanType
			}
		}

		return c.booleanType
	case token.Equals: // =
		checkAssignmentOperator(rightType)
		c.getNodeLinks(node).resolvedFlowType = c.getNodeLinks(node.Right).resolvedFlowType
		return rightType
	}

	return nil
}

func (c *checker) checkConditionalExpression(node *ast.ConditionalExpression, contextualMapper *TypeMapper) Type {
	c.checkExpression(node.Condition, nil)
	var type1 = c.checkExpression(node.WhenTrue, contextualMapper)
	var type2 = c.checkExpression(node.WhenFalse, contextualMapper)
	var contextualType Type
	if c.isInferentialContext(contextualMapper) {
		contextualType = nil
	} else {
		contextualType = c.GetContextualType(node.Condition)
	}
	var resultType = c.getBestCommonType([]Type{type1, type2}, contextualType, true)
	if resultType == nil {
		if contextualType != nil {
			c.error(node, diagnostic.M_No_best_common_type_exists_between_0_1_and_2,
				c.typeToString(contextualType, nil, true),
				c.typeToString(type1, nil, true),
				c.typeToString(type2, nil, true))
		} else {
			c.error(node, diagnostic.M_No_best_common_type_exists_between_0_and_1,
				c.typeToString(type1, nil, true),
				c.typeToString(type2, nil, true))
		}
		resultType = c.unknownType
	}
	return resultType
}

func getCharLiteralFirstRuneValue(text string) string {
	if len(text) == 0 {
		return "0"
	}

	r, size := utf8.DecodeLastRuneInString(text)
	if size == 0 {
		return "0"
	}
	return strconv.Itoa(int(r))
}

func (c *checker) checkLiteralExpression(node *ast.LiteralExpression) Type {
	switch node.Token {
	case token.CharLiteral,
		token.IntLiteral,
		token.LongLiteral,
		token.FloatLiteral,
		token.DoubleLiteral:
		c.checkGrammarNumericLikeLiteral(node)
	}

	switch node.Token {
	case token.NullKeyword:
		return c.nullType
	case token.TrueKeyword:
		c.getNodeLinks(node).resolvedFlowType = c.trueType
		return c.booleanType
	case token.FalseKeyword:
		c.getNodeLinks(node).resolvedFlowType = c.falseType
		return c.booleanType
	case token.CharLiteral:
		var text = getCharLiteralFirstRuneValue(node.Value)
		c.getNodeLinks(node).resolvedFlowType = c.getDigitalLiteralTypeForText(text)
		return c.charType
	case token.IntLiteral:
		c.getNodeLinks(node).resolvedFlowType = c.getDigitalLiteralTypeForText(node.Value)
		return c.intType
	case token.LongLiteral:
		c.getNodeLinks(node).resolvedFlowType = c.getDigitalLiteralTypeForText(node.Value)
		return c.longType
	case token.FloatLiteral:
		c.getNodeLinks(node).resolvedFlowType = c.getDecimalLiteralTypeForText(node.Value)
		return c.floatType
	case token.DoubleLiteral:
		c.getNodeLinks(node).resolvedFlowType = c.getDecimalLiteralTypeForText(node.Value)
		return c.doubleType
	case token.StringLiteral:
		c.getNodeLinks(node).resolvedFlowType = c.getStringLiteralTypeForType(node.Value)
		return c.globalStringType
	}
	panic("Not run to here.")
}

func (c *checker) checkExpressionWithContextualType(node ast.Expression, contextualType Type, contextualMapper *TypeMapper) Type {
	var link = c.getNodeLinks(node)
	var saveContextualType = link.contextualType
	link.contextualType = contextualType
	var result = c.checkExpression(node, contextualMapper)
	link.contextualType = saveContextualType
	return result
}

func (c *checker) checkExpressionCached(node ast.Expression, contextualMapper *TypeMapper) Type {
	var links = c.getNodeLinks(node)
	if links.resolvedType == nil {
		// When computing a type that we're going to cache, we need to ignore any ongoing control flow
		// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
		// to the top of the stack ensures all transient types are computed from a known point.
		var saveFlowLoopStart = c.flowLoopStart
		c.flowLoopStart = c.flowLoopCount
		links.resolvedType = c.checkExpression(node, contextualMapper)
		c.flowLoopStart = saveFlowLoopStart
	}
	return links.resolvedType
}

func (c *checker) isTypeAssertion(node ast.Expression) bool {
	var source = ast.SkipParenthesizedExpression(node)
	return ast.IsAsExpression(source)
}

func (c *checker) checkDeclarationInitializer(declaration ast.VariableLike) Type {
	var tpe = c.checkExpressionCached(declaration.GetInitializer(), nil)
	if ast.GetCombinedModifierFlags(declaration)&ast.MFFinal != 0 && c.isTypeAssertion(declaration.GetInitializer()){
		return tpe
	}
	return tpe
}

func (c *checker) getContainerClassOrEnum(node ast.Node) ast.Node {
	for !isNull(node) {
		if ast.IsClassDeclaration(node) ||
			ast.IsEnumDeclaration(node) {
			return node
		}

		node = node.Parent()
	}

	return nil
}

// Checks an expression and returns its type_. The contextualMapper parameter serves two purposes: When
// contextualMapper is not undefined and not equal to the identityMapper function  object it indicates that the
// expression is being inferentially typed (section 4.12.2 in spec) and provides the type mapper to use in
// conjunction with the generic contextual type_. When contextualMapper is equal to the identityMapper function
// object, it serves as an indicator that all contained function  and arrow expressions should be considered to
// have the wildcard function  type; this form of type check is used during overload resolution to exclude
// contextually typed function  and arrow expressions in the initial phase.
func (c *checker) checkExpression(node ast.Expression, contextualMapper *TypeMapper) Type {
	var tpe = c.checkExpressionWorker(node, contextualMapper)
	if contextualMapper != nil && contextualMapper != c.identityMapper {
		var signature = c.getSingleCallSignature(tpe)
		if signature != nil && signature.TypeParameters != nil {
			var contextualType = c.GetContextualType(node)
			if contextualType != nil {
				var contextualSignature = c.getSingleCallSignature(contextualType)
				if contextualSignature != nil && contextualSignature.TypeParameters == nil {
					tpe = c.getOrCreateTypeFromSignature(c.instantiateSignatureInContextOf(signature, contextualSignature, contextualMapper))
				}
			}
		}
	}
	return tpe
}

func (c *checker) checkExpressionWorker(node ast.Expression, contextualMapper *TypeMapper) Type {
	switch n := node.(type) {
	case *ast.Identifier:
		return c.checkIdentifier(n)
	case *ast.ThisExpression:
		return c.checkThisExpression(n)
	case *ast.SuperExpression:
		return c.checkSuperExpression(n)
	case *ast.LiteralExpression:
		return c.checkLiteralExpression(n)
	case *ast.ArrayLiteralExpression:
		return c.checkArrayLiteral(n, nil)
	case *ast.ClassExpression:
		return c.checkClassExpression(n)
	case *ast.QualifiedName:
		return c.checkQualifiedName(n)
	case *ast.SelectorExpression:
		return c.checkSelectorExpression(n)
	case *ast.InstanceAccessExpression:
		return c.checkInstanceAccessExpression(n)
	case *ast.ElementAccessExpression:
		return c.checkIndexedAccess(n)
	case *ast.DocumentCallExpression:
		return c.checkDocumentCallExpression(n)
	case *ast.CallExpression:
		return c.checkCallExpression(n)
	case *ast.ConstructorExpression:
		return c.checkConstructExpression(n)
	case *ast.QualifiedExpression:
		return c.checkQualifiedExpression(n)
	case *ast.NewObjectExpression:
		return c.checkNewObjectExpression(n)
	case *ast.NewArrayExpression:
		return c.checkNewArrayExpression(n)
	case *ast.ParenthesizedExpression:
		return c.checkParenthesizedExpression(n, contextualMapper)
	case *ast.ArrowExpression:
		return c.checkArrowExpression(n, contextualMapper)
	case *ast.AsExpression:
		return c.checkAssertion(n)
	case *ast.PrefixUnaryExpression:
		return c.checkPreUnaryExpr(n)
	case *ast.PostfixUnaryExpression:
		return c.checkPostUnaryExpr(n)
	case *ast.BinaryExpression:
		return c.checkBinaryExpression(n, contextualMapper)
	case *ast.ConditionalExpression:
		return c.checkConditionalExpression(n, contextualMapper)
	}
	return c.unknownType
}
